JBossWeb SVN: r2044 - in trunk/java/org/apache/catalina: session and 1 other directory.
by jbossweb-commits@lists.jboss.org
Author: remy.maucherat(a)jboss.com
Date: 2012-06-27 11:29:53 -0400 (Wed, 27 Jun 2012)
New Revision: 2044
Modified:
trunk/java/org/apache/catalina/core/StandardContext.java
trunk/java/org/apache/catalina/session/StandardSession.java
Log:
- Port minor listeners optimizations.
Modified: trunk/java/org/apache/catalina/core/StandardContext.java
===================================================================
--- trunk/java/org/apache/catalina/core/StandardContext.java 2012-06-18 11:43:11 UTC (rev 2043)
+++ trunk/java/org/apache/catalina/core/StandardContext.java 2012-06-27 15:29:53 UTC (rev 2044)
@@ -3375,7 +3375,7 @@
getLogger().debug("Sending application start events");
Object instances[] = getApplicationLifecycleListeners();
- if (instances == null)
+ if (instances == null || instances.length == 0)
return (ok);
ServletContextEvent event =
new ServletContextEvent(getServletContext());
Modified: trunk/java/org/apache/catalina/session/StandardSession.java
===================================================================
--- trunk/java/org/apache/catalina/session/StandardSession.java 2012-06-18 11:43:11 UTC (rev 2043)
+++ trunk/java/org/apache/catalina/session/StandardSession.java 2012-06-27 15:29:53 UTC (rev 2044)
@@ -361,7 +361,7 @@
// Notify interested application event listeners
Context context = (Context) manager.getContainer();
Object listeners[] = context.getApplicationSessionLifecycleListeners();
- if (listeners != null) {
+ if (listeners != null && listeners.length > 0) {
HttpSessionEvent event =
new HttpSessionEvent(getSession());
for (int i = 0; i < listeners.length; i++) {
12 years, 5 months
JBossWeb SVN: r2043 - branches/7.0.x/webapps/docs and 2 other directories.
by jbossweb-commits@lists.jboss.org
Author: remy.maucherat(a)jboss.com
Date: 2012-06-18 07:43:11 -0400 (Mon, 18 Jun 2012)
New Revision: 2043
Modified:
branches/7.0.x/java/org/apache/jasper/runtime/PerThreadTagHandlerPool.java
branches/7.0.x/java/org/apache/jasper/runtime/TagHandlerPool.java
branches/7.0.x/webapps/docs/changelog.xml
trunk/java/org/apache/jasper/runtime/PerThreadTagHandlerPool.java
trunk/java/org/apache/jasper/runtime/TagHandlerPool.java
trunk/webapps/docs/changelog.xml
Log:
JBWEB-242: Fix rare leak with tag pooling
Modified: branches/7.0.x/java/org/apache/jasper/runtime/PerThreadTagHandlerPool.java
===================================================================
--- branches/7.0.x/java/org/apache/jasper/runtime/PerThreadTagHandlerPool.java 2012-06-14 14:11:34 UTC (rev 2042)
+++ branches/7.0.x/java/org/apache/jasper/runtime/PerThreadTagHandlerPool.java 2012-06-18 11:43:11 UTC (rev 2043)
@@ -119,7 +119,17 @@
if (ptd.current < (ptd.handlers.length - 1)) {
ptd.handlers[++ptd.current] = handler;
} else {
- handler.release();
+ try {
+ handler.release();
+ } finally {
+ if (Constants.INJECT_TAGS || Constants.USE_INSTANCE_MANAGER_FOR_TAGS) {
+ try {
+ instanceManager.destroyInstance(handler);
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
+ }
}
}
Modified: branches/7.0.x/java/org/apache/jasper/runtime/TagHandlerPool.java
===================================================================
--- branches/7.0.x/java/org/apache/jasper/runtime/TagHandlerPool.java 2012-06-14 14:11:34 UTC (rev 2042)
+++ branches/7.0.x/java/org/apache/jasper/runtime/TagHandlerPool.java 2012-06-18 11:43:11 UTC (rev 2043)
@@ -148,6 +148,13 @@
}
// There is no need for other threads to wait for us to release
handler.release();
+ if (Constants.INJECT_TAGS || Constants.USE_INSTANCE_MANAGER_FOR_TAGS) {
+ try {
+ instanceManager.destroyInstance(handler);
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
}
/**
Modified: branches/7.0.x/webapps/docs/changelog.xml
===================================================================
--- branches/7.0.x/webapps/docs/changelog.xml 2012-06-14 14:11:34 UTC (rev 2042)
+++ branches/7.0.x/webapps/docs/changelog.xml 2012-06-18 11:43:11 UTC (rev 2043)
@@ -45,6 +45,9 @@
<fix>
Use per webapp expression factory. (markt)
</fix>
+ <fix>
+ <jira>242</jira>: Fix rare leak with tag pooling. (remm)
+ </fix>
</changelog>
</subsection>
</section>
Modified: trunk/java/org/apache/jasper/runtime/PerThreadTagHandlerPool.java
===================================================================
--- trunk/java/org/apache/jasper/runtime/PerThreadTagHandlerPool.java 2012-06-14 14:11:34 UTC (rev 2042)
+++ trunk/java/org/apache/jasper/runtime/PerThreadTagHandlerPool.java 2012-06-18 11:43:11 UTC (rev 2043)
@@ -119,7 +119,17 @@
if (ptd.current < (ptd.handlers.length - 1)) {
ptd.handlers[++ptd.current] = handler;
} else {
- handler.release();
+ try {
+ handler.release();
+ } finally {
+ if (Constants.INJECT_TAGS || Constants.USE_INSTANCE_MANAGER_FOR_TAGS) {
+ try {
+ instanceManager.destroyInstance(handler);
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
+ }
}
}
Modified: trunk/java/org/apache/jasper/runtime/TagHandlerPool.java
===================================================================
--- trunk/java/org/apache/jasper/runtime/TagHandlerPool.java 2012-06-14 14:11:34 UTC (rev 2042)
+++ trunk/java/org/apache/jasper/runtime/TagHandlerPool.java 2012-06-18 11:43:11 UTC (rev 2043)
@@ -148,6 +148,13 @@
}
// There is no need for other threads to wait for us to release
handler.release();
+ if (Constants.INJECT_TAGS || Constants.USE_INSTANCE_MANAGER_FOR_TAGS) {
+ try {
+ instanceManager.destroyInstance(handler);
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
}
/**
Modified: trunk/webapps/docs/changelog.xml
===================================================================
--- trunk/webapps/docs/changelog.xml 2012-06-14 14:11:34 UTC (rev 2042)
+++ trunk/webapps/docs/changelog.xml 2012-06-18 11:43:11 UTC (rev 2043)
@@ -78,6 +78,9 @@
<fix>
Use per webapp expression factory. (markt)
</fix>
+ <fix>
+ <jira>242</jira>: Fix rare leak with tag pooling. (remm)
+ </fix>
</changelog>
</subsection>
</section>
12 years, 6 months
JBossWeb SVN: r2042 - in trunk: java/org/apache/catalina/connector and 5 other directories.
by jbossweb-commits@lists.jboss.org
Author: remy.maucherat(a)jboss.com
Date: 2012-06-14 10:11:34 -0400 (Thu, 14 Jun 2012)
New Revision: 2042
Removed:
trunk/java/org/apache/coyote/ajp/AjpProcessor.java
trunk/java/org/apache/coyote/ajp/AjpProtocol.java
trunk/java/org/apache/coyote/http11/Http11Processor.java
trunk/java/org/apache/coyote/http11/Http11Protocol.java
trunk/java/org/apache/coyote/http11/InternalInputBuffer.java
trunk/java/org/apache/coyote/http11/InternalOutputBuffer.java
trunk/java/org/apache/tomcat/util/net/DefaultServerSocketFactory.java
trunk/java/org/apache/tomcat/util/net/JIoEndpoint.java
trunk/java/org/apache/tomcat/util/net/ServerSocketFactory.java
trunk/java/org/apache/tomcat/util/net/jsse/JSSEFactory.java
trunk/java/org/apache/tomcat/util/net/jsse/JSSEImplementation.java
trunk/java/org/apache/tomcat/util/net/jsse/JSSESocketFactory.java
trunk/java/org/apache/tomcat/util/net/jsse/JSSESupport.java
Modified:
trunk/build.properties.default
trunk/java/org/apache/catalina/connector/Connector.java
trunk/java/org/apache/coyote/http11/Http11AbstractProcessor.java
trunk/java/org/apache/tomcat/util/net/SSLImplementation.java
trunk/java/org/apache/tomcat/util/net/jsse/NioJSSEImplementation.java
trunk/webapps/docs/changelog.xml
Log:
Drop java.io connectors (AJP and HTTP). AJP now requires native.
Modified: trunk/build.properties.default
===================================================================
--- trunk/build.properties.default 2012-06-13 08:50:17 UTC (rev 2041)
+++ trunk/build.properties.default 2012-06-14 14:11:34 UTC (rev 2042)
@@ -11,7 +11,7 @@
# ----- Version Control Flags -----
version.major=7
-version.minor=0
+version.minor=7
version.build=0
version.patch=0
version.tag=SNAPSHOT
Modified: trunk/java/org/apache/catalina/connector/Connector.java
===================================================================
--- trunk/java/org/apache/catalina/connector/Connector.java 2012-06-13 08:50:17 UTC (rev 2041)
+++ trunk/java/org/apache/catalina/connector/Connector.java 2012-06-14 14:11:34 UTC (rev 2042)
@@ -594,10 +594,7 @@
} else {
if ("HTTP/1.1".equals(protocol) || "http".equals(protocol)) {
setProtocolHandlerClassName
- ("org.apache.coyote.http11.Http11Protocol");
- } else if ("AJP/1.3".equals(protocol) || "ajp".equals(protocol)) {
- setProtocolHandlerClassName
- ("org.apache.coyote.ajp.AjpProtocol");
+ ("org.apache.coyote.http11.Http11NioProtocol");
} else if (protocol != null) {
setProtocolHandlerClassName(protocol);
}
Deleted: trunk/java/org/apache/coyote/ajp/AjpProcessor.java
===================================================================
--- trunk/java/org/apache/coyote/ajp/AjpProcessor.java 2012-06-13 08:50:17 UTC (rev 2041)
+++ trunk/java/org/apache/coyote/ajp/AjpProcessor.java 2012-06-14 14:11:34 UTC (rev 2042)
@@ -1,1350 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source
- * Copyright 2009, JBoss Inc., and individual contributors as indicated
- * by the @authors tag. See the copyright.txt 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.ajp;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InterruptedIOException;
-import java.io.OutputStream;
-import java.net.InetAddress;
-import java.net.Socket;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-
-import org.apache.coyote.ActionCode;
-import org.apache.coyote.ActionHook;
-import org.apache.coyote.Adapter;
-import org.apache.coyote.InputBuffer;
-import org.apache.coyote.OutputBuffer;
-import org.apache.coyote.Request;
-import org.apache.coyote.RequestInfo;
-import org.apache.coyote.Response;
-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.HttpMessages;
-import org.apache.tomcat.util.http.MimeHeaders;
-import org.apache.tomcat.util.net.JIoEndpoint;
-import org.apache.tomcat.util.net.SocketStatus;
-import org.apache.tomcat.util.net.JIoEndpoint.Handler.SocketState;
-import org.apache.tomcat.util.res.StringManager;
-
-
-/**
- * Processes HTTP requests.
- *
- * @author Remy Maucherat
- * @author Henri Gomez
- * @author Dan Milstein
- * @author Keith Wannamaker
- * @author Kevin Seguin
- * @author Costin Manolache
- * @author Bill Barker
- */
-public class AjpProcessor implements ActionHook {
-
-
- /**
- * Logger.
- */
- protected static org.jboss.logging.Logger log
- = org.jboss.logging.Logger.getLogger(AjpProcessor.class);
-
- /**
- * The string manager for this package.
- */
- protected static StringManager sm =
- StringManager.getManager(Constants.Package);
-
-
- // ----------------------------------------------------------- Constructors
-
-
- public AjpProcessor(int packetSize, JIoEndpoint endpoint) {
-
- this.endpoint = endpoint;
-
- request = new Request();
- request.setInputBuffer(new SocketInputBuffer());
-
- response = new Response();
- response.setHook(this);
- response.setOutputBuffer(new SocketOutputBuffer());
- request.setResponse(response);
-
- requestHeaderMessage = new AjpMessage(packetSize);
- responseHeaderMessage = new AjpMessage(packetSize);
- bodyMessage = new AjpMessage(packetSize);
-
- // Set the get body message buffer
- AjpMessage getBodyMessage = new AjpMessage(16);
- getBodyMessage.reset();
- getBodyMessage.appendByte(Constants.JK_AJP13_GET_BODY_CHUNK);
- getBodyMessage.appendInt(packetSize - Constants.READ_HEAD_LEN);
- getBodyMessage.end();
- getBodyMessageArray = new byte[getBodyMessage.getLen()];
- System.arraycopy(getBodyMessage.getBuffer(), 0, getBodyMessageArray,
- 0, getBodyMessage.getLen());
-
- // Cause loading of HexUtils
- int foo = HexUtils.DEC[0];
-
- // Cause loading of HttpMessages
- HttpMessages.getMessage(200);
-
- }
-
-
- // ----------------------------------------------------- Instance Variables
-
-
- /**
- * Associated adapter.
- */
- protected Adapter adapter = null;
-
-
- /**
- * Request object.
- */
- protected Request request = null;
-
-
- /**
- * Response object.
- */
- protected Response response = null;
-
-
- /**
- * Header message. Note that this header is merely the one used during the
- * processing of the first message of a "request", so it might not be a request
- * header. It will stay unchanged during the processing of the whole request.
- */
- protected AjpMessage requestHeaderMessage = null;
-
-
- /**
- * Message used for response header composition.
- */
- protected AjpMessage responseHeaderMessage = null;
-
-
- /**
- * Body message.
- */
- protected AjpMessage bodyMessage = null;
-
-
- /**
- * Body message.
- */
- protected MessageBytes bodyBytes = MessageBytes.newInstance();
-
-
- /**
- * Error flag.
- */
- protected boolean error = false;
-
-
- /**
- * Socket associated with the current connection.
- */
- protected Socket socket;
-
-
- /**
- * Input stream.
- */
- protected InputStream input;
-
-
- /**
- * Output stream.
- */
- protected OutputStream output;
-
-
- /**
- * Host name (used to avoid useless B2C conversion on the host name).
- */
- protected char[] hostNameC = new char[0];
-
-
- /**
- * Associated endpoint.
- */
- protected JIoEndpoint endpoint;
-
-
- /**
- * The socket timeout used when reading the first block of the request
- * header.
- */
- protected long readTimeout;
-
-
- /**
- * Temp message bytes used for processing.
- */
- protected MessageBytes tmpMB = MessageBytes.newInstance();
-
-
- /**
- * Byte chunk for certs.
- */
- protected MessageBytes certificates = MessageBytes.newInstance();
-
-
- /**
- * End of stream flag.
- */
- protected boolean endOfStream = false;
-
-
- /**
- * Body empty flag.
- */
- protected boolean empty = true;
-
-
- /**
- * First read.
- */
- protected boolean first = true;
-
-
- /**
- * Replay read.
- */
- protected boolean replay = false;
-
-
- /**
- * Finished response.
- */
- protected boolean finished = false;
-
-
- /**
- * Direct buffer used for sending right away a get body message.
- */
- protected final byte[] getBodyMessageArray;
-
-
- /**
- * Direct buffer used for sending right away a pong message.
- */
- protected static final byte[] pongMessageArray;
-
-
- /**
- * End message array.
- */
- protected static final byte[] endMessageArray;
-
- /**
- * Flush message array.
- */
- protected static final byte[] flushMessageArray;
-
-
- /**
- * Event used.
- */
- protected boolean event = false;
-
-
- /**
- * Event processing.
- */
- protected boolean eventProcessing = true;
- public void startProcessing() { eventProcessing = true; }
- public void endProcessing() { eventProcessing = false; }
-
-
- // ----------------------------------------------------- Static Initializer
-
-
- static {
-
- // Set the read body message buffer
- AjpMessage pongMessage = new AjpMessage(16);
- pongMessage.reset();
- pongMessage.appendByte(Constants.JK_AJP13_CPONG_REPLY);
- pongMessage.end();
- pongMessageArray = new byte[pongMessage.getLen()];
- System.arraycopy(pongMessage.getBuffer(), 0, pongMessageArray,
- 0, pongMessage.getLen());
-
- // Allocate the end message array
- AjpMessage endMessage = new AjpMessage(16);
- endMessage.reset();
- endMessage.appendByte(Constants.JK_AJP13_END_RESPONSE);
- endMessage.appendByte(1);
- endMessage.end();
- endMessageArray = new byte[endMessage.getLen()];
- System.arraycopy(endMessage.getBuffer(), 0, endMessageArray, 0,
- endMessage.getLen());
-
- // Allocate the flush message array
- AjpMessage flushMessage = new AjpMessage(16);
- flushMessage.reset();
- flushMessage.appendByte(Constants.JK_AJP13_SEND_BODY_CHUNK);
- flushMessage.appendInt(0);
- flushMessage.appendByte(0);
- flushMessage.end();
- flushMessageArray = new byte[flushMessage.getLen()];
- System.arraycopy(flushMessage.getBuffer(), 0, flushMessageArray, 0,
- flushMessage.getLen());
-
- }
-
-
- // ------------------------------------------------------------- Properties
-
-
- /**
- * Use Tomcat authentication ?
- */
- protected boolean tomcatAuthentication = true;
- public boolean getTomcatAuthentication() { return tomcatAuthentication; }
- public void setTomcatAuthentication(boolean tomcatAuthentication) { this.tomcatAuthentication = tomcatAuthentication; }
-
-
- /**
- * Required secret.
- */
- protected String requiredSecret = null;
- public void setRequiredSecret(String requiredSecret) { this.requiredSecret = requiredSecret; }
-
-
- /**
- * The number of milliseconds Tomcat will wait for a subsequent request
- * before closing the connection. The default is the same as for
- * Apache HTTP Server (15 000 milliseconds).
- */
- protected int keepAliveTimeout = -1;
- public int getKeepAliveTimeout() { return keepAliveTimeout; }
- public void setKeepAliveTimeout(int timeout) { keepAliveTimeout = timeout; }
-
-
- /**
- * Timeout.
- */
- protected int timeout = -1;
- public void setTimeout(int timeout) { this.timeout = timeout; }
- public int getTimeout() { return timeout; }
-
-
- /**
- * A resume has been requested.
- */
- protected boolean resumeNotification = false;
- public boolean getResumeNotification() { return resumeNotification; }
-
-
- // --------------------------------------------------------- Public Methods
-
-
- /** Get the request associated with this processor.
- *
- * @return The request
- */
- public Request getRequest() {
- return request;
- }
-
-
- public SocketState event(SocketStatus status)
- throws IOException {
-
- RequestInfo rp = request.getRequestProcessor();
- try {
- 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;
- }
- 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) {
- recycle();
- return SocketState.CLOSED;
- } else if (!event) {
- finish();
- recycle();
- return SocketState.OPEN;
- } else {
- return SocketState.LONG;
- }
- }
-
-
- /**
- * Process pipelined HTTP requests using the specified input and output
- * streams.
- *
- * @throws IOException error during an I/O operation
- */
- public SocketState process(Socket socket)
- throws IOException {
- RequestInfo rp = request.getRequestProcessor();
- rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
-
- // Setting up the socket
- this.socket = socket;
- input = socket.getInputStream();
- output = socket.getOutputStream();
- int soTimeout = -1;
- if (keepAliveTimeout > 0) {
- soTimeout = socket.getSoTimeout();
- }
-
- // Error flag
- error = false;
-
- while (!error && !event) {
-
- // Parsing the request header
- try {
- // Set keep alive timeout if enabled
- if (keepAliveTimeout > 0) {
- socket.setSoTimeout(keepAliveTimeout);
- }
- // Get first message of the request
- if (!readMessage(requestHeaderMessage)) {
- // This means a connection timeout
- rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
- break;
- }
- // Set back timeout if keep alive timeout is enabled
- if (keepAliveTimeout > 0) {
- socket.setSoTimeout(soTimeout);
- }
- // Check message type, process right away and break if
- // not regular request processing
- int type = requestHeaderMessage.getByte();
- if (type == Constants.JK_AJP13_CPING_REQUEST) {
- try {
- output.write(pongMessageArray);
- } catch (IOException e) {
- error = true;
- }
- continue;
- } else if(type != Constants.JK_AJP13_FORWARD_REQUEST) {
- // Usually the servlet didn't read the previous request body
- if(log.isDebugEnabled()) {
- log.debug("Unexpected message: "+type);
- }
- error = true;
- break;
- }
-
- request.setStartTime(System.currentTimeMillis());
- } catch (IOException e) {
- error = true;
- break;
- } catch (Throwable t) {
- log.debug(sm.getString("ajpprocessor.header.error"), 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) {
- log.debug(sm.getString("ajpprocessor.request.prepare"), t);
- // 400 - Internal Server Error
- response.setStatus(400);
- error = true;
- }
-
- // Process the request in the adapter
- if (!error) {
- try {
- rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
- adapter.service(request, response);
- } catch (InterruptedIOException e) {
- error = true;
- } catch (Throwable t) {
- log.error(sm.getString("ajpprocessor.request.process"), t);
- // 500 - Internal Server Error
- response.setStatus(500);
- error = true;
- }
- }
-
- // Finish the response if not done yet
- if (!event && !finished) {
- try {
- finish();
- } catch (Throwable t) {
- error = true;
- }
- }
-
- // 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();
-
- if (!event) {
- recycle();
- }
-
- rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
-
- }
-
- rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
-
- if (event) {
- if (error) {
- input = null;
- output = null;
- recycle();
- return SocketState.CLOSED;
- } else {
- eventProcessing = false;
- return SocketState.LONG;
- }
- } else {
- input = null;
- output = null;
- recycle();
- return SocketState.CLOSED;
- }
- }
-
-
- // ----------------------------------------------------- ActionHook Methods
-
-
- /**
- * Send an action to the connector.
- *
- * @param actionCode Type of the action
- * @param param Action parameter
- */
- public void action(ActionCode actionCode, Object param) {
-
- if (actionCode == ActionCode.ACTION_COMMIT) {
-
- if (response.isCommitted())
- return;
-
- // Validate and write response headers
- try {
- prepareResponse();
- } catch (IOException e) {
- // Set error flag
- error = true;
- }
-
- } else if (actionCode == ActionCode.ACTION_CLIENT_FLUSH) {
-
- if (!response.isCommitted()) {
- // Validate and write response headers
- try {
- prepareResponse();
- } catch (IOException e) {
- // Set error flag
- error = true;
- return;
- }
- }
-
- try {
- flush();
- } catch (IOException e) {
- // Set error flag
- error = true;
- }
-
- } else if (actionCode == ActionCode.ACTION_CLOSE) {
- // Close
-
- // End the processing of the current request, and stop any further
- // transactions with the client
-
- try {
- finish();
- } catch (IOException e) {
- // Set error flag
- error = true;
- }
-
- } else if (actionCode == ActionCode.ACTION_REQ_SSL_ATTRIBUTE ) {
-
- if (!certificates.isNull()) {
- ByteChunk certData = certificates.getByteChunk();
- X509Certificate jsseCerts[] = null;
- ByteArrayInputStream bais =
- new ByteArrayInputStream(certData.getBytes(),
- certData.getStart(),
- certData.getLength());
- // Fill the elements.
- try {
- CertificateFactory cf =
- CertificateFactory.getInstance("X.509");
- while(bais.available() > 0) {
- X509Certificate cert = (X509Certificate)
- cf.generateCertificate(bais);
- if(jsseCerts == null) {
- jsseCerts = new X509Certificate[1];
- jsseCerts[0] = cert;
- } else {
- X509Certificate [] temp = new X509Certificate[jsseCerts.length+1];
- System.arraycopy(jsseCerts,0,temp,0,jsseCerts.length);
- temp[jsseCerts.length] = cert;
- jsseCerts = temp;
- }
- }
- } catch (java.security.cert.CertificateException e) {
- log.error(sm.getString("ajpprocessor.certs.fail"), e);
- return;
- }
- request.setAttribute(org.apache.tomcat.util.net.Constants.CERTIFICATE_KEY, jsseCerts);
- }
-
- } else if (actionCode == ActionCode.ACTION_REQ_HOST_ATTRIBUTE) {
-
- // Get remote host name using a DNS resolution
- if (request.remoteHost().isNull()) {
- try {
- request.remoteHost().setString(InetAddress.getByName
- (request.remoteAddr().toString()).getHostName());
- } catch (IOException iex) {
- // Ignore
- }
- }
-
- } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_ADDR_ATTRIBUTE) {
-
- // Copy from local name for now, which should simply be an address
- request.localAddr().setString(request.localName().toString());
-
- } else if (actionCode == ActionCode.ACTION_REQ_SET_BODY_REPLAY) {
-
- // Set the given bytes as the content
- ByteChunk bc = (ByteChunk) param;
- int length = bc.getLength();
- bodyBytes.setBytes(bc.getBytes(), bc.getStart(), length);
- request.setContentLength(length);
- first = false;
- empty = false;
- replay = true;
- endOfStream = false;
-
- } else if (actionCode == ActionCode.ACTION_EVENT_BEGIN) {
- event = true;
- } else if (actionCode == ActionCode.ACTION_EVENT_END) {
- event = false;
- } else if (actionCode == ActionCode.ACTION_EVENT_SUSPEND) {
- // No action needed
- } else if (actionCode == ActionCode.ACTION_EVENT_RESUME) {
- // An event is being processed already: adding for resume will be done
- // when the socket gets back to the poller
- if (!eventProcessing && !resumeNotification) {
- endpoint.getEventPoller().add(socket, timeout, true, true);
- }
- resumeNotification = true;
- } else if (actionCode == ActionCode.ACTION_EVENT_TIMEOUT) {
- timeout = ((Integer) param).intValue();
- }
-
-
- }
-
-
- // ------------------------------------------------------ Connector Methods
-
-
- /**
- * Set the associated adapter.
- *
- * @param adapter the new adapter
- */
- public void setAdapter(Adapter adapter) {
- this.adapter = adapter;
- }
-
-
- /**
- * Get the associated adapter.
- *
- * @return the associated adapter
- */
- public Adapter getAdapter() {
- return adapter;
- }
-
-
- // ------------------------------------------------------ Protected Methods
-
-
- /**
- * After reading the request headers, we have to setup the request filters.
- */
- protected void prepareRequest() {
-
- // Translate the HTTP method code to a String.
- byte methodCode = requestHeaderMessage.getByte();
- if (methodCode != Constants.SC_M_JK_STORED) {
- String methodName = Constants.methodTransArray[(int)methodCode - 1];
- request.method().setString(methodName);
- }
-
- requestHeaderMessage.getBytes(request.protocol());
- requestHeaderMessage.getBytes(request.requestURI());
-
- requestHeaderMessage.getBytes(request.remoteAddr());
- requestHeaderMessage.getBytes(request.remoteHost());
- requestHeaderMessage.getBytes(request.localName());
- request.setLocalPort(requestHeaderMessage.getInt());
-
- boolean isSSL = requestHeaderMessage.getByte() != 0;
- if (isSSL) {
- request.scheme().setString("https");
- }
-
- // Decode headers
- MimeHeaders headers = request.getMimeHeaders();
-
- int hCount = requestHeaderMessage.getInt();
- for(int i = 0 ; i < hCount ; i++) {
- String hName = null;
-
- // Header names are encoded as either an integer code starting
- // with 0xA0, or as a normal string (in which case the first
- // two bytes are the length).
- int isc = requestHeaderMessage.peekInt();
- int hId = isc & 0xFF;
-
- MessageBytes vMB = null;
- isc &= 0xFF00;
- if(0xA000 == isc) {
- requestHeaderMessage.getInt(); // To advance the read position
- hName = Constants.headerTransArray[hId - 1];
- vMB = headers.addValue(hName);
- } else {
- // reset hId -- if the header currently being read
- // happens to be 7 or 8 bytes long, the code below
- // will think it's the content-type header or the
- // content-length header - SC_REQ_CONTENT_TYPE=7,
- // SC_REQ_CONTENT_LENGTH=8 - leading to unexpected
- // behaviour. see bug 5861 for more information.
- hId = -1;
- requestHeaderMessage.getBytes(tmpMB);
- ByteChunk bc = tmpMB.getByteChunk();
- vMB = headers.addValue(bc.getBuffer(),
- bc.getStart(), bc.getLength());
- }
-
- requestHeaderMessage.getBytes(vMB);
-
- if (hId == Constants.SC_REQ_CONTENT_LENGTH ||
- (hId == -1 && tmpMB.equalsIgnoreCase("Content-Length"))) {
- // just read the content-length header, so set it
- request.setContentLength( vMB.getInt() );
- } else if (hId == Constants.SC_REQ_CONTENT_TYPE ||
- (hId == -1 && tmpMB.equalsIgnoreCase("Content-Type"))) {
- // just read the content-type header, so set it
- ByteChunk bchunk = vMB.getByteChunk();
- request.contentType().setBytes(bchunk.getBytes(),
- bchunk.getOffset(),
- bchunk.getLength());
- }
- }
-
- // Decode extra attributes
- boolean secret = false;
- byte attributeCode;
- while ((attributeCode = requestHeaderMessage.getByte())
- != Constants.SC_A_ARE_DONE) {
-
- switch (attributeCode) {
-
- case Constants.SC_A_REQ_ATTRIBUTE :
- requestHeaderMessage.getBytes(tmpMB);
- String n = tmpMB.toString();
- requestHeaderMessage.getBytes(tmpMB);
- String v = tmpMB.toString();
- request.setAttribute(n, v);
- break;
-
- case Constants.SC_A_CONTEXT :
- requestHeaderMessage.getBytes(tmpMB);
- // nothing
- break;
-
- case Constants.SC_A_SERVLET_PATH :
- requestHeaderMessage.getBytes(tmpMB);
- // nothing
- break;
-
- case Constants.SC_A_REMOTE_USER :
- if (tomcatAuthentication) {
- // ignore server
- requestHeaderMessage.getBytes(tmpMB);
- } else {
- requestHeaderMessage.getBytes(request.getRemoteUser());
- }
- break;
-
- case Constants.SC_A_AUTH_TYPE :
- if (tomcatAuthentication) {
- // ignore server
- requestHeaderMessage.getBytes(tmpMB);
- } else {
- requestHeaderMessage.getBytes(request.getAuthType());
- }
- break;
-
- case Constants.SC_A_QUERY_STRING :
- requestHeaderMessage.getBytes(request.queryString());
- break;
-
- case Constants.SC_A_JVM_ROUTE :
- requestHeaderMessage.getBytes(request.instanceId());
- break;
-
- case Constants.SC_A_SSL_CERT :
- request.scheme().setString("https");
- // SSL certificate extraction is lazy, moved to JkCoyoteHandler
- requestHeaderMessage.getBytes(certificates);
- break;
-
- case Constants.SC_A_SSL_CIPHER :
- request.scheme().setString("https");
- requestHeaderMessage.getBytes(tmpMB);
- request.setAttribute(org.apache.tomcat.util.net.Constants.CIPHER_SUITE_KEY,
- tmpMB.toString());
- break;
-
- case Constants.SC_A_SSL_SESSION :
- request.scheme().setString("https");
- requestHeaderMessage.getBytes(tmpMB);
- request.setAttribute(org.apache.tomcat.util.net.Constants.SESSION_ID_KEY,
- tmpMB.toString());
- break;
-
- case Constants.SC_A_SSL_KEY_SIZE :
- request.setAttribute(org.apache.tomcat.util.net.Constants.KEY_SIZE_KEY,
- new Integer(requestHeaderMessage.getInt()));
- break;
-
- case Constants.SC_A_STORED_METHOD:
- requestHeaderMessage.getBytes(request.method());
- break;
-
- case Constants.SC_A_SECRET:
- requestHeaderMessage.getBytes(tmpMB);
- if (requiredSecret != null) {
- secret = true;
- if (!tmpMB.equals(requiredSecret)) {
- response.setStatus(403);
- error = true;
- }
- }
- break;
-
- default:
- // Ignore unknown attribute for backward compatibility
- break;
-
- }
-
- }
-
- // Check if secret was submitted if required
- if ((requiredSecret != null) && !secret) {
- response.setStatus(403);
- error = true;
- }
-
- // 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);
- }
-
- }
-
- MessageBytes valueMB = request.getMimeHeaders().getValue("host");
- parseHost(valueMB);
-
- }
-
-
- /**
- * Parse host.
- */
- public void parseHost(MessageBytes valueMB) {
-
- if (valueMB == null || (valueMB != null && valueMB.isNull()) ) {
- // HTTP/1.0
- request.setServerPort(request.getLocalPort());
- try {
- request.serverName().duplicate(request.localName());
- } catch (IOException e) {
- response.setStatus(400);
- error = true;
- }
- 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 (request.scheme().equalsIgnoreCase("https")) {
- // 443 - Default HTTPS port
- request.setServerPort(443);
- } else {
- // 80 - Default HTTTP port
- request.setServerPort(80);
- }
- 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[(int) 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);
-
- }
-
- }
-
-
- /**
- * When committing the response, we have to validate the set of headers, as
- * well as setup the response filters.
- */
- protected void prepareResponse()
- throws IOException {
-
- response.setCommitted(true);
-
- responseHeaderMessage.reset();
- responseHeaderMessage.appendByte(Constants.JK_AJP13_SEND_HEADERS);
-
- // HTTP header contents
- responseHeaderMessage.appendInt(response.getStatus());
- String message = null;
- if (org.apache.coyote.Constants.USE_CUSTOM_STATUS_MSG_IN_HEADER) {
- message = response.getMessage();
- }
- if (message == null){
- message = HttpMessages.getMessage(response.getStatus());
- } else {
- message = message.replace('\n', ' ').replace('\r', ' ');
- }
- if (message == null) {
- // Many httpd 2.x wants a non empty status message
- message = Integer.toString(response.getStatus());
- }
- tmpMB.setString(message);
- responseHeaderMessage.appendBytes(tmpMB);
-
- // Special headers
- MimeHeaders headers = response.getMimeHeaders();
- 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);
- }
- int contentLength = response.getContentLength();
- if (contentLength >= 0) {
- headers.setValue("Content-Length").setInt(contentLength);
- }
-
- // Other headers
- int numHeaders = headers.size();
- responseHeaderMessage.appendInt(numHeaders);
- for (int i = 0; i < numHeaders; i++) {
- MessageBytes hN = headers.getName(i);
- MessageBytes hV=headers.getValue(i);
- if (hN.getLength() > 0 && !hV.isNull()) {
- int hC = Constants.getResponseAjpIndex(hN.toString());
- if (hC > 0) {
- responseHeaderMessage.appendInt(hC);
- }
- else {
- responseHeaderMessage.appendBytes(hN);
- }
- responseHeaderMessage.appendBytes(hV);
- }
- }
-
- // Write to buffer
- responseHeaderMessage.end();
- output.write(responseHeaderMessage.getBuffer(), 0, responseHeaderMessage.getLen());
-
- }
-
-
- /**
- * Finish AJP response.
- */
- protected void finish()
- throws IOException {
-
- if (!response.isCommitted()) {
- // Validate and write response headers
- try {
- prepareResponse();
- } catch (IOException e) {
- // Set error flag
- error = true;
- }
- }
-
- if (finished)
- return;
-
- finished = true;
-
- // Add the end message
- output.write(endMessageArray);
-
-
- // read remaining data from the special first-body-chunk
- if (first && request.getContentLengthLong() > 0) {
- try {
- receive();
- } catch (IOException e) {
- }
- }
-
- }
-
-
- /**
- * Read at least the specified amount of bytes, and place them
- * in the input buffer.
- */
- protected boolean read(byte[] buf, int pos, int n)
- throws IOException {
-
- int read = 0;
- int res = 0;
- while (read < n) {
- res = input.read(buf, read + pos, n - read);
- if (res > 0) {
- read += res;
- } else {
- throw new IOException(sm.getString("ajpprotocol.failedread"));
- }
- }
-
- return true;
-
- }
-
-
- /** Receive a chunk of data. Called to implement the
- * 'special' packet in ajp13 and to receive the data
- * after we send a GET_BODY packet
- */
- public boolean receive() throws IOException {
-
- first = false;
- bodyMessage.reset();
- readMessage(bodyMessage);
-
- // No data received.
- if (bodyMessage.getLen() == 0) {
- // just the header
- // Don't mark 'end of stream' for the first chunk.
- return false;
- }
- int blen = bodyMessage.peekInt();
- if (blen == 0) {
- return false;
- }
-
- bodyMessage.getBytes(bodyBytes);
- empty = false;
- return true;
- }
-
- /**
- * Get more request body data from the web server and store it in the
- * internal buffer.
- *
- * @return true if there is more data, false if not.
- */
- private boolean refillReadBuffer() throws IOException {
- // If the server returns an empty packet, assume that that end of
- // the stream has been reached (yuck -- fix protocol??).
- // FORM support
- if (replay) {
- endOfStream = true; // we've read everything there is
- }
- if (endOfStream) {
- return false;
- }
- if (finished) {
- return false;
- }
-
- // Request more data immediately
- output.write(getBodyMessageArray);
-
- boolean moreData = receive();
- if( !moreData ) {
- endOfStream = true;
- }
- return moreData;
- }
-
-
- /**
- * Read an AJP message.
- *
- * @return true if the message has been read, false if the short read
- * didn't return anything
- * @throws IOException any other failure, including incomplete reads
- */
- protected boolean readMessage(AjpMessage message)
- throws IOException {
-
- byte[] buf = message.getBuffer();
-
- read(buf, 0, message.getHeaderLength());
-
- if (message.processHeader() < 0) {
- throw new IOException(sm.getString("ajpprotocol.badmessage"));
- }
- read(buf, message.getHeaderLength(), message.getLen());
-
- return true;
-
- }
-
-
- /**
- * Recycle the processor.
- */
- public void recycle() {
-
- // Recycle Request object
- first = true;
- endOfStream = false;
- empty = true;
- replay = false;
- finished = false;
- timeout = -1;
- resumeNotification = false;
- eventProcessing = true;
- request.recycle();
- response.recycle();
- certificates.recycle();
-
- }
-
-
- /**
- * Callback to write data from the buffer.
- */
- protected void flush()
- throws IOException {
- // Send the flush message
- output.write(flushMessageArray);
- }
-
-
- // ------------------------------------- InputStreamInputBuffer Inner Class
-
-
- /**
- * This class is an input buffer which will read its data from an input
- * stream.
- */
- protected class SocketInputBuffer
- implements InputBuffer {
-
-
- /**
- * Read bytes into the specified chunk.
- */
- public int doRead(ByteChunk chunk, Request req )
- throws IOException {
-
- if (endOfStream) {
- return -1;
- }
- if (first && req.getContentLength() > 0) {
- // Handle special first-body-chunk
- if (!receive()) {
- return 0;
- }
- } else if (empty) {
- if (!refillReadBuffer()) {
- return -1;
- }
- }
- ByteChunk bc = bodyBytes.getByteChunk();
- chunk.setBytes(bc.getBuffer(), bc.getStart(), bc.getLength());
- empty = true;
- return chunk.getLength();
-
- }
-
- }
-
-
- // ----------------------------------- OutputStreamOutputBuffer Inner Class
-
-
- /**
- * This class is an output buffer which will write data to an output
- * stream.
- */
- protected class SocketOutputBuffer
- implements OutputBuffer {
-
-
- /**
- * Write chunk.
- */
- public int doWrite(ByteChunk chunk, Response res)
- throws IOException {
-
- if (!response.isCommitted()) {
- // Validate and write response headers
- try {
- prepareResponse();
- } catch (IOException e) {
- // Set error flag
- error = true;
- }
- }
-
- int len = chunk.getLength();
- // 4 - hardcoded, byte[] marshalling overhead
- int chunkSize = Constants.MAX_SEND_SIZE;
- int off = 0;
- while (len > 0) {
- int thisTime = len;
- if (thisTime > chunkSize) {
- thisTime = chunkSize;
- }
- len -= thisTime;
- responseHeaderMessage.reset();
- responseHeaderMessage.appendByte(Constants.JK_AJP13_SEND_BODY_CHUNK);
- responseHeaderMessage.appendBytes(chunk.getBytes(), chunk.getOffset() + off, thisTime);
- responseHeaderMessage.end();
- output.write(responseHeaderMessage.getBuffer(), 0, responseHeaderMessage.getLen());
-
- off += thisTime;
- }
-
- return chunk.getLength();
-
- }
-
-
- }
-
-
-}
Deleted: trunk/java/org/apache/coyote/ajp/AjpProtocol.java
===================================================================
--- trunk/java/org/apache/coyote/ajp/AjpProtocol.java 2012-06-13 08:50:17 UTC (rev 2041)
+++ trunk/java/org/apache/coyote/ajp/AjpProtocol.java 2012-06-14 14:11:34 UTC (rev 2042)
@@ -1,577 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source
- * Copyright 2009, JBoss Inc., and individual contributors as indicated
- * by the @authors tag. See the copyright.txt 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.ajp;
-
-import java.net.InetAddress;
-import java.net.Socket;
-import java.net.URLEncoder;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
-
-import javax.management.MBeanRegistration;
-import javax.management.MBeanServer;
-import javax.management.ObjectName;
-
-import org.apache.coyote.Adapter;
-import org.apache.coyote.ProtocolHandler;
-import org.apache.coyote.RequestGroupInfo;
-import org.apache.coyote.RequestInfo;
-import org.apache.tomcat.util.modeler.Registry;
-import org.apache.tomcat.util.net.JIoEndpoint;
-import org.apache.tomcat.util.net.SocketStatus;
-import org.apache.tomcat.util.net.JIoEndpoint.Handler;
-import org.apache.tomcat.util.res.StringManager;
-
-
-/**
- * Abstract the protocol implementation, including threading, etc.
- * Processor is single threaded and specific to stream-based protocols,
- * will not fit Jk protocols like JNI.
- *
- * @author Remy Maucherat
- * @author Costin Manolache
- */
-public class AjpProtocol
- implements ProtocolHandler, MBeanRegistration {
-
-
- protected static org.jboss.logging.Logger log =
- org.jboss.logging.Logger.getLogger(AjpProtocol.class);
-
- /**
- * The string manager for this package.
- */
- protected static StringManager sm =
- StringManager.getManager(Constants.Package);
-
-
- // ------------------------------------------------------------ Constructor
-
-
- public AjpProtocol() {
- cHandler = new AjpConnectionHandler(this);
- setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
- setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
- //setServerSoTimeout(Constants.DEFAULT_SERVER_SOCKET_TIMEOUT);
- setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
- }
-
-
- // ----------------------------------------------------- Instance Variables
-
-
- protected ObjectName tpOname;
-
-
- protected ObjectName rgOname;
-
-
- /**
- * Associated java.io endpoint.
- */
- protected JIoEndpoint endpoint = new JIoEndpoint();
-
-
- /**
- * Configuration attributes.
- */
- protected Hashtable attributes = new Hashtable();
-
-
- /**
- * Adapter which will process the requests recieved by this endpoint.
- */
- private Adapter adapter;
-
-
- /**
- * Connection handler for AJP.
- */
- private AjpConnectionHandler cHandler;
-
-
- // --------------------------------------------------------- Public Methods
-
-
- /**
- * Pass config info
- */
- public void setAttribute(String name, Object value) {
- if (log.isTraceEnabled()) {
- log.trace(sm.getString("ajpprotocol.setattribute", name, value));
- }
- attributes.put(name, value);
- }
-
- public Object getAttribute(String key) {
- if (log.isTraceEnabled()) {
- log.trace(sm.getString("ajpprotocol.getattribute", key));
- }
- return attributes.get(key);
- }
-
-
- public Iterator getAttributeNames() {
- return attributes.keySet().iterator();
- }
-
-
- /**
- * The adapter, used to call the connector
- */
- public void setAdapter(Adapter adapter) {
- this.adapter = adapter;
- }
-
-
- public Adapter getAdapter() {
- return adapter;
- }
-
-
- public boolean hasIoEvents() {
- return false;
- }
-
- public RequestGroupInfo getRequestGroupInfo() {
- return cHandler.global;
- }
-
-
- /** Start the protocol
- */
- public void init() throws Exception {
- endpoint.setName(getName());
- endpoint.setHandler(cHandler);
-
- try {
- endpoint.init();
- } catch (Exception ex) {
- log.error(sm.getString("ajpprotocol.endpoint.initerror"), ex);
- throw ex;
- }
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("ajpprotocol.init", getName()));
- }
- }
-
-
- 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("ajpprotocol.endpoint.starterror"), ex);
- throw ex;
- }
- if (log.isInfoEnabled())
- log.info(sm.getString("ajpprotocol.start", getName()));
- }
-
- public void pause() throws Exception {
- try {
- endpoint.pause();
- } catch (Exception ex) {
- log.error(sm.getString("ajpprotocol.endpoint.pauseerror"), ex);
- throw ex;
- }
- // 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) {
- ;
- }
- done = false;
- break;
- }
- }
- }
- if (log.isInfoEnabled())
- log.info(sm.getString("ajpprotocol.pause", getName()));
- }
-
- public void resume() throws Exception {
- try {
- endpoint.resume();
- } catch (Exception ex) {
- log.error(sm.getString("ajpprotocol.endpoint.resumeerror"), ex);
- throw ex;
- }
- if (log.isInfoEnabled())
- log.info(sm.getString("ajpprotocol.resume", getName()));
- }
-
- public void destroy() throws Exception {
- if (log.isInfoEnabled())
- log.info(sm.getString("ajpprotocol.stop", getName()));
- endpoint.destroy();
- 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);
- }
- }
-
- public String getJmxName() {
- String encodedAddr = "";
- if (getAddress() != null) {
- encodedAddr = "" + getAddress();
- encodedAddr = URLEncoder.encode(encodedAddr.replace('/', '-').replace(':', '_').replace('%', '-')) + "-";
- }
- return ("ajp-" + encodedAddr + endpoint.getPort());
- }
-
- public String getName() {
- String encodedAddr = "";
- if (getAddress() != null) {
- encodedAddr = getAddress() + ":";
- }
- return ("ajp-" + encodedAddr + endpoint.getPort());
- }
-
- /**
- * Processor cache.
- */
- protected int processorCache = -1;
- public int getProcessorCache() { return this.processorCache; }
- public void setProcessorCache(int processorCache) { this.processorCache = processorCache; }
-
- public Executor getExecutor() { return endpoint.getExecutor(); }
- public void setExecutor(Executor executor) { endpoint.setExecutor(executor); }
-
- public int getMaxThreads() { return endpoint.getMaxThreads(); }
- public void setMaxThreads(int maxThreads) { endpoint.setMaxThreads(maxThreads); }
-
- public int getThreadPriority() { return endpoint.getThreadPriority(); }
- public void setThreadPriority(int threadPriority) { endpoint.setThreadPriority(threadPriority); }
-
- public int getBacklog() { return endpoint.getBacklog(); }
- public void setBacklog(int backlog) { endpoint.setBacklog(backlog); }
-
- public int getPort() { return endpoint.getPort(); }
- public void setPort(int port) { endpoint.setPort(port); }
-
- public InetAddress getAddress() { return endpoint.getAddress(); }
- public void setAddress(InetAddress ia) { endpoint.setAddress(ia); }
-
- public boolean getTcpNoDelay() { return endpoint.getTcpNoDelay(); }
- public void setTcpNoDelay(boolean tcpNoDelay) { endpoint.setTcpNoDelay(tcpNoDelay); }
-
- public int getSoLinger() { return endpoint.getSoLinger(); }
- public void setSoLinger(int soLinger) { endpoint.setSoLinger(soLinger); }
-
- public int getSoTimeout() { return endpoint.getSoTimeout(); }
- public void setSoTimeout(int soTimeout) { endpoint.setSoTimeout(soTimeout); }
-
- public void setPollerSize(int pollerSize) { endpoint.setPollerSize(pollerSize); }
- public int getPollerSize() { return endpoint.getPollerSize(); }
-
- /**
- * Should authentication be done in the native webserver layer,
- * or in the Servlet container ?
- */
- protected boolean tomcatAuthentication = true;
- public boolean getTomcatAuthentication() { return tomcatAuthentication; }
- public void setTomcatAuthentication(boolean tomcatAuthentication) { this.tomcatAuthentication = tomcatAuthentication; }
-
- /**
- * Required secret.
- */
- protected String requiredSecret = null;
- public void setRequiredSecret(String requiredSecret) { this.requiredSecret = requiredSecret; }
-
- /**
- * AJP packet size.
- */
- protected int packetSize = Constants.MAX_PACKET_SIZE;
- public int getPacketSize() { return packetSize; }
- public void setPacketSize(int packetSize) { this.packetSize = packetSize; }
-
-
- /**
- * The number of seconds Tomcat will wait for a subsequent request
- * before closing the connection.
- */
- protected int keepAliveTimeout = -1;
- public int getKeepAliveTimeout() { return keepAliveTimeout; }
- public void setKeepAliveTimeout(int timeout) { keepAliveTimeout = timeout; }
-
-
- // -------------------------------------- AjpConnectionHandler Inner Class
-
-
- protected static class AjpConnectionHandler implements Handler {
-
- protected AjpProtocol proto;
- protected AtomicLong registerCount = new AtomicLong(0);
- protected RequestGroupInfo global = new RequestGroupInfo();
-
- protected ConcurrentHashMap<Socket, AjpProcessor> connections =
- new ConcurrentHashMap<Socket, AjpProcessor>();
- protected ConcurrentLinkedQueue<AjpProcessor> recycledProcessors =
- new ConcurrentLinkedQueue<AjpProcessor>() {
- protected AtomicInteger size = new AtomicInteger(0);
- public boolean offer(AjpProcessor 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;
- }
-
- public AjpProcessor poll() {
- AjpProcessor result = super.poll();
- if ( result != null ) {
- size.decrementAndGet();
- }
- return result;
- }
-
- public void clear() {
- AjpProcessor next = poll();
- while ( next != null ) {
- unregister(next);
- next = poll();
- }
- super.clear();
- size.set(0);
- }
- };
-
- public AjpConnectionHandler(AjpProtocol proto) {
- this.proto = proto;
- }
-
- public SocketState event(Socket socket, SocketStatus status) {
- AjpProcessor result = connections.get(socket);
- SocketState state = SocketState.CLOSED;
- if (result != null) {
- result.startProcessing();
- // Call the appropriate event
- try {
- state = result.event(status);
- } catch (java.net.SocketException e) {
- // SocketExceptions are normal
- AjpProcessor.log.debug
- (sm.getString
- ("ajpprotocol.proto.socketexception.debug"), e);
- } catch (java.io.IOException e) {
- // IOExceptions are normal
- AjpProcessor.log.debug
- (sm.getString
- ("ajpprotocol.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.
- AjpProcessor.log.error
- (sm.getString("ajpprotocol.proto.error"), e);
- } finally {
- if (state != SocketState.LONG) {
- connections.remove(socket);
- recycledProcessors.offer(result);
- } else {
- if (proto.endpoint.isRunning()) {
- proto.endpoint.getEventPoller().add(socket, result.getTimeout(),
- result.getResumeNotification(), false);
- }
- }
- result.endProcessing();
- }
- }
- return state;
- }
-
- public SocketState process(Socket socket) {
- AjpProcessor processor = recycledProcessors.poll();
- try {
-
- if (processor == null) {
- processor = createProcessor();
- }
-
- SocketState state = processor.process(socket);
- 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(socket, processor);
- proto.endpoint.getEventPoller().add(socket, processor.getTimeout(),
- processor.getResumeNotification(), false);
- } else {
- recycledProcessors.offer(processor);
- }
- return state;
-
- } catch(java.net.SocketException e) {
- // SocketExceptions are normal
- AjpProtocol.log.debug
- (sm.getString
- ("ajpprotocol.proto.socketexception.debug"), e);
- } catch (java.io.IOException e) {
- // IOExceptions are normal
- AjpProtocol.log.debug
- (sm.getString
- ("ajpprotocol.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.
- AjpProtocol.log.error
- (sm.getString("ajpprotocol.proto.error"), e);
- }
- recycledProcessors.offer(processor);
- return SocketState.CLOSED;
- }
-
- protected AjpProcessor createProcessor() {
- AjpProcessor processor = new AjpProcessor(proto.packetSize, proto.endpoint);
- processor.setAdapter(proto.adapter);
- processor.setTomcatAuthentication(proto.tomcatAuthentication);
- processor.setRequiredSecret(proto.requiredSecret);
- processor.setKeepAliveTimeout(proto.keepAliveTimeout);
- register(processor);
- return processor;
- }
-
- protected void register(AjpProcessor 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.getJmxName() + ",name=AjpRequest" + count);
- if (log.isDebugEnabled()) {
- log.debug("Register " + rpName);
- }
- Registry.getRegistry(null, null).registerComponent(rp, rpName, null);
- rp.setRpName(rpName);
- } catch (Exception e) {
- log.warn("Error registering request");
- }
- }
- }
- }
-
- protected void unregister(AjpProcessor 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) {
- log.warn("Error unregistering request", e);
- }
- }
- }
- }
-
- }
-
-
- // -------------------- Various implementation classes --------------------
-
-
- protected String domain;
- protected ObjectName oname;
- protected MBeanServer mserver;
-
- public ObjectName getObjectName() {
- return oname;
- }
-
- public String getDomain() {
- return domain;
- }
-
- public ObjectName preRegister(MBeanServer server,
- ObjectName name) throws Exception {
- oname=name;
- mserver=server;
- domain=name.getDomain();
- return name;
- }
-
- public void postRegister(Boolean registrationDone) {
- }
-
- public void preDeregister() throws Exception {
- }
-
- public void postDeregister() {
- }
-
-
-}
Modified: trunk/java/org/apache/coyote/http11/Http11AbstractProcessor.java
===================================================================
--- trunk/java/org/apache/coyote/http11/Http11AbstractProcessor.java 2012-06-13 08:50:17 UTC (rev 2041)
+++ trunk/java/org/apache/coyote/http11/Http11AbstractProcessor.java 2012-06-14 14:11:34 UTC (rev 2042)
@@ -51,7 +51,7 @@
* Logger.
*/
protected static org.jboss.logging.Logger log = org.jboss.logging.Logger
- .getLogger(Http11Processor.class);
+ .getLogger(Http11AbstractProcessor.class);
/**
* The string manager for this package.
Deleted: trunk/java/org/apache/coyote/http11/Http11Processor.java
===================================================================
--- trunk/java/org/apache/coyote/http11/Http11Processor.java 2012-06-13 08:50:17 UTC (rev 2041)
+++ trunk/java/org/apache/coyote/http11/Http11Processor.java 2012-06-14 14:11:34 UTC (rev 2042)
@@ -1,1745 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source
- * Copyright 2009, JBoss Inc., and individual contributors as indicated
- * by the @authors tag. See the copyright.txt 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.InterruptedIOException;
-import java.net.InetAddress;
-import java.net.Socket;
-import java.util.Locale;
-import java.util.StringTokenizer;
-import java.util.regex.Pattern;
-import java.util.regex.PatternSyntaxException;
-
-import org.apache.coyote.ActionCode;
-import org.apache.coyote.ActionHook;
-import org.apache.coyote.Adapter;
-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.Ascii;
-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.JIoEndpoint;
-import org.apache.tomcat.util.net.SSLSupport;
-import org.apache.tomcat.util.net.SocketStatus;
-import org.apache.tomcat.util.net.JIoEndpoint.Handler.SocketState;
-import org.apache.tomcat.util.res.StringManager;
-
-
-/**
- * Processes HTTP requests.
- *
- * @author Remy Maucherat
- */
-public class Http11Processor 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();
-
-
- // ------------------------------------------------------------ Constructor
-
-
- public Http11Processor(int headerBufferSize, JIoEndpoint endpoint) {
-
- this.endpoint = endpoint;
-
- request = new Request();
- inputBuffer = new InternalInputBuffer(request, headerBufferSize);
- request.setInputBuffer(inputBuffer);
-
- response = new Response();
- response.setHook(this);
- outputBuffer = new InternalOutputBuffer(response, headerBufferSize);
- response.setOutputBuffer(outputBuffer);
- request.setResponse(response);
-
- initializeFilters();
-
- // Cause loading of HexUtils
- int foo = HexUtils.DEC[0];
-
- // Cause loading of FastHttpDateFormat
- FastHttpDateFormat.getCurrentDate();
-
- }
-
-
- // ----------------------------------------------------- Instance Variables
-
-
- /**
- * Associated adapter.
- */
- protected Adapter adapter = null;
-
-
- /**
- * Request object.
- */
- protected Request request = null;
-
-
- /**
- * Response object.
- */
- protected Response response = null;
-
-
- /**
- * Input.
- */
- protected InternalInputBuffer inputBuffer = null;
-
-
- /**
- * Output.
- */
- protected InternalOutputBuffer outputBuffer = 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;
-
-
- /**
- * Socket associated with the current connection.
- */
- protected Socket socket;
-
-
- /**
- * 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;
-
-
- /**
- * Socket buffering.
- */
- protected int socketBuffer = -1;
-
-
- /**
- * 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];
-
-
- /**
- * Associated endpoint.
- */
- protected JIoEndpoint endpoint;
-
-
- /**
- * Allow a customized the server header for the tin-foil hat folks.
- */
- protected String server = null;
-
-
- /**
- * Event used.
- */
- protected boolean event = false;
-
-
- /**
- * True if a resume has been requested.
- */
- protected boolean resumeNotification = false;
-
-
- /**
- * Event processing.
- */
- protected boolean eventProcessing = true;
- public void startProcessing() { eventProcessing = true; }
- public void endProcessing() { eventProcessing = false; }
-
-
- // ------------------------------------------------------------- Properties
-
-
- /**
- * 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.
- */
- 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;
- }
- }
- }
-
- /**
- * Set Minimum size to trigger compression.
- */
- public void setCompressionMinSize(int compressionMinSize) {
- this.compressionMinSize = compressionMinSize;
- }
-
-
- /**
- * 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 (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).
- */
- public void setNoCompressionUserAgents(Pattern[] noCompressionUserAgents) {
- this.noCompressionUserAgents = noCompressionUserAgents;
- }
-
-
- /**
- * Set no compression user agent list.
- * List contains users agents separated by ',' :
- *
- * ie: "gorilla,desesplorer,tigrus"
- */
- 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).
- */
- 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"
- */
- 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);
- }
-
-
- /**
- * Timeout.
- */
- protected int timeout = -1;
- public void setTimeout(int timeout) { this.timeout = timeout; }
- public int getTimeout() { return timeout; }
-
-
- // --------------------------------------------------------- Public Methods
-
-
- /**
- * 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
- */
- private 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
- */
- private 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
- */
- private 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 (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).
- */
- public void setRestrictedUserAgents(Pattern[] restrictedUserAgents) {
- this.restrictedUserAgents = restrictedUserAgents;
- }
-
-
- /**
- * 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"
- */
- 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 maximum number of Keep-Alive requests to honor.
- * This is to safeguard from DoS attacks. Setting to a negative
- * value disables the check.
- */
- public void setMaxKeepAliveRequests(int mkar) {
- maxKeepAliveRequests = mkar;
- }
-
-
- /**
- * Return the number of Keep-Alive requests that we will honor.
- */
- public int getMaxKeepAliveRequests() {
- return maxKeepAliveRequests;
- }
-
- /**
- * Set the Keep-Alive timeout.
- */
- public void setKeepAliveTimeout(int timeout) {
- keepAliveTimeout = timeout;
- }
-
-
- /**
- * Return the number Keep-Alive timeout.
- */
- public int getKeepAliveTimeout() {
- return keepAliveTimeout;
- }
-
-
- /**
- * Set the maximum size of a POST which will be buffered in SSL mode.
- */
- public void setMaxSavePostSize(int msps) {
- maxSavePostSize = msps;
- }
-
-
- /**
- * Return the maximum size of a POST which will be buffered in SSL mode.
- */
- public int getMaxSavePostSize() {
- return maxSavePostSize;
- }
-
-
- /**
- * Set the SSL information for this HTTP connection.
- */
- public void setSSLSupport(SSLSupport sslSupport) {
- this.sslSupport = sslSupport;
- }
-
-
- /**
- * Set the flag to control upload time-outs.
- */
- public void setDisableUploadTimeout(boolean isDisabled) {
- disableUploadTimeout = isDisabled;
- }
-
- /**
- * Get the flag that controls upload time-outs.
- */
- public boolean getDisableUploadTimeout() {
- return disableUploadTimeout;
- }
-
- public boolean getResumeNotification() {
- return resumeNotification;
- }
-
-
- /**
- * Set the socket buffer flag.
- */
- public void setSocketBuffer(int socketBuffer) {
- this.socketBuffer = socketBuffer;
- outputBuffer.setSocketBuffer(socketBuffer);
- }
-
- /**
- * Get the socket buffer flag.
- */
- public int getSocketBuffer() {
- return socketBuffer;
- }
-
-
- /**
- * Set the server header name.
- */
- public void setServer( String server ) {
- if (server==null || server.equals("")) {
- this.server = null;
- } else {
- this.server = server;
- }
- }
-
- /**
- * Get the server header name.
- */
- public String getServer() {
- return server;
- }
-
-
- /** Get the request associated with this processor.
- *
- * @return The request
- */
- public Request getRequest() {
- return request;
- }
-
- public SocketState event(SocketStatus status)
- throws IOException {
-
- RequestInfo rp = request.getRequestProcessor();
- try {
- 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;
- }
- 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 on the specified socket.
- *
- * @param socket Socket from which the HTTP requests will be read
- * and the HTTP responses will be written.
- *
- * @throws IOException error during an I/O operation
- */
- public SocketState process(Socket socket)
- throws IOException {
- RequestInfo rp = request.getRequestProcessor();
- rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
-
- // Set the remote address
- remoteAddr = null;
- remoteHost = null;
- localAddr = null;
- localName = null;
- remotePort = -1;
- localPort = -1;
-
- // Setting up the I/O
- this.socket = socket;
- inputBuffer.setInputStream(socket.getInputStream());
- outputBuffer.setOutputStream(socket.getOutputStream());
-
- // Error flag
- error = false;
- keepAlive = true;
-
- int keepAliveLeft = maxKeepAliveRequests;
- int soTimeout = socket.getSoTimeout();
-
- int threadRatio = (endpoint.getCurrentThreadsBusy() * 100)
- / endpoint.getMaxThreads();
- if (threadRatio > 75) {
- keepAliveLeft = 1;
- }
-
- boolean keptAlive = false;
-
- while (!error && keepAlive && !event) {
-
- // Parsing the request header
- try {
- if (!disableUploadTimeout && keptAlive) {
- if (keepAliveTimeout > 0) {
- socket.setSoTimeout(keepAliveTimeout);
- }
- else if (soTimeout > 0) {
- socket.setSoTimeout(soTimeout);
- }
- }
- inputBuffer.parseRequestLine();
- request.setStartTime(System.currentTimeMillis());
- keptAlive = true;
- if (!disableUploadTimeout) {
- socket.setSoTimeout(timeout);
- }
- 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);
- }
- // 400 - Internal Server Error
- response.setStatus(400);
- 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();
-
- if (!event) {
- // Next request
- inputBuffer.nextRequest();
- outputBuffer.nextRequest();
- }
-
- 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 SocketState.CLOSED;
- }
-
- }
-
-
- 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;
- }
-
- }
-
-
- public void recycle() {
- inputBuffer.recycle();
- outputBuffer.recycle();
- this.socket = null;
- sslSupport = null;
- timeout = -1;
- resumeNotification = false;
- eventProcessing = true;
- }
-
-
- // ----------------------------------------------------- ActionHook Methods
-
-
- /**
- * Send an action to the connector.
- *
- * @param actionCode Type of the action
- * @param param Action parameter
- */
- public void action(ActionCode actionCode, Object param) {
-
- if (actionCode == ActionCode.ACTION_COMMIT) {
- // Commit current response
-
- if (response.isCommitted())
- return;
-
- // Validate and write response headers
- prepareResponse();
- try {
- outputBuffer.commit();
- } catch (IOException e) {
- // Set error flag
- error = true;
- }
-
- } else if (actionCode == ActionCode.ACTION_ACK) {
-
- // Acknowlege request
-
- // Send a 100 status back if it makes sense (response not committed
- // yet, and client specified an expectation for 100-continue)
-
- if ((response.isCommitted()) || !expectation)
- return;
-
- inputBuffer.setSwallowInput(true);
- try {
- outputBuffer.sendAck();
- } catch (IOException e) {
- // Set error flag
- error = true;
- }
-
- } else if (actionCode == ActionCode.ACTION_CLIENT_FLUSH) {
-
- try {
- outputBuffer.flush();
- } catch (IOException e) {
- // Set error flag
- error = true;
- response.setErrorException(e);
- }
-
- } else if (actionCode == ActionCode.ACTION_CLOSE) {
- // Close
-
- // End the processing of the current request, and stop any further
- // transactions with the client
-
- try {
- outputBuffer.endRequest();
- } catch (IOException e) {
- // Set error flag
- error = true;
- }
-
- } else if (actionCode == ActionCode.ACTION_CUSTOM) {
-
- // Do nothing
-
- } else if (actionCode == ActionCode.ACTION_REQ_SSL_ATTRIBUTE ) {
-
- 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);
- }
-
- } else if (actionCode == ActionCode.ACTION_REQ_HOST_ADDR_ATTRIBUTE) {
-
- if ((remoteAddr == null) && (socket != null)) {
- InetAddress inetAddr = socket.getInetAddress();
- if (inetAddr != null) {
- remoteAddr = inetAddr.getHostAddress();
- }
- }
- request.remoteAddr().setString(remoteAddr);
-
- } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_NAME_ATTRIBUTE) {
-
- if ((localName == null) && (socket != null)) {
- InetAddress inetAddr = socket.getLocalAddress();
- if (inetAddr != null) {
- localName = inetAddr.getHostName();
- }
- }
- request.localName().setString(localName);
-
- } else if (actionCode == ActionCode.ACTION_REQ_HOST_ATTRIBUTE) {
-
- if ((remoteHost == null) && (socket != null)) {
- InetAddress inetAddr = socket.getInetAddress();
- if (inetAddr != null) {
- remoteHost = inetAddr.getHostName();
- }
- if(remoteHost == null) {
- if(remoteAddr != null) {
- remoteHost = remoteAddr;
- } else { // all we can do is punt
- request.remoteHost().recycle();
- }
- }
- }
- request.remoteHost().setString(remoteHost);
-
- } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_ADDR_ATTRIBUTE) {
-
- if (localAddr == null)
- localAddr = socket.getLocalAddress().getHostAddress();
-
- request.localAddr().setString(localAddr);
-
- } else if (actionCode == ActionCode.ACTION_REQ_REMOTEPORT_ATTRIBUTE) {
-
- if ((remotePort == -1 ) && (socket !=null)) {
- remotePort = socket.getPort();
- }
- request.setRemotePort(remotePort);
-
- } else if (actionCode == ActionCode.ACTION_REQ_LOCALPORT_ATTRIBUTE) {
-
- if ((localPort == -1 ) && (socket !=null)) {
- localPort = socket.getLocalPort();
- }
- request.setLocalPort(localPort);
-
- } else if (actionCode == ActionCode.ACTION_REQ_SSL_CERTIFICATE) {
- 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);
- }
- }
- } else if (actionCode == ActionCode.ACTION_REQ_SET_BODY_REPLAY) {
- ByteChunk body = (ByteChunk) param;
-
- InputFilter savedBody = new SavedRequestInputFilter(body);
- savedBody.setRequest(request);
-
- InternalInputBuffer internalBuffer = (InternalInputBuffer)
- request.getInputBuffer();
- internalBuffer.addActiveFilter(savedBody);
- } else if (actionCode == ActionCode.ACTION_EVENT_BEGIN) {
- event = true;
- } else if (actionCode == ActionCode.ACTION_EVENT_END) {
- event = false;
- } else if (actionCode == ActionCode.ACTION_EVENT_SUSPEND) {
- // No action needed
- } else if (actionCode == ActionCode.ACTION_EVENT_RESUME) {
- // An event is being processed already: adding for resume will be done
- // when the socket gets back to the poller
- if (!eventProcessing && !resumeNotification) {
- endpoint.getEventPoller().add(socket, timeout, true, true);
- }
- resumeNotification = true;
- } else if (actionCode == ActionCode.ACTION_EVENT_TIMEOUT) {
- timeout = ((Integer) param).intValue();
- }
-
- }
-
-
- // ------------------------------------------------------ Connector Methods
-
-
- /**
- * Set the associated adapter.
- *
- * @param adapter the new adapter
- */
- public void setAdapter(Adapter adapter) {
- this.adapter = adapter;
- }
-
-
- /**
- * Get the associated adapter.
- *
- * @return the associated adapter
- */
- public Adapter getAdapter() {
- return adapter;
- }
-
-
- // ------------------------------------------------------ Protected Methods
-
-
- /**
- * After reading the request headers, we have to setup the request filters.
- */
- protected void prepareRequest() {
-
- http11 = true;
- http09 = false;
- contentDelimitation = false;
- expectation = false;
- if (sslSupport != null) {
- 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
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("http11processor.request.prepare")+
- " Unsupported HTTP version \""+protocolMB+"\"");
- }
- 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
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("http11processor.request.prepare")+
- " Unsupported transfer encoding \""+encodingName+"\"");
- }
- 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
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("http11processor.request.prepare")+
- " host header missing");
- }
- 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(socket.getLocalPort());
- InetAddress localAddress = socket.getLocalAddress();
- // Setting the socket-related fields. The adapter doesn't know
- // about socket.
- request.serverName().setString(localAddress.getHostName());
- 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 (sslSupport == null) {
- // 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);
-
- }
-
- }
-
-
- /**
- * Check for compression
- */
- private 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;
- }
-
-
- /**
- * When committing the response, we have to validate the set of headers, as
- * well as setup the response filters.
- */
- 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;
- }
-
- MessageBytes methodMB = request.method();
- if (methodMB.equals("HEAD")) {
- // No entity body
- outputBuffer.addActiveFilter
- (outputFilters[Constants.VOID_FILTER]);
- contentDelimitation = true;
- }
-
- // Check for compression
- boolean useCompression = false;
- if (entityBody && (compressionLevel > 0)) {
- 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();
-
- }
-
-
- /**
- * Initialize standard input and output filters.
- */
- 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;
- }
-
-
- /**
- * 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;
-
- }
-
- /**
- * 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 */;
- }
-
-}
Deleted: trunk/java/org/apache/coyote/http11/Http11Protocol.java
===================================================================
--- trunk/java/org/apache/coyote/http11/Http11Protocol.java 2012-06-13 08:50:17 UTC (rev 2041)
+++ trunk/java/org/apache/coyote/http11/Http11Protocol.java 2012-06-14 14:11:34 UTC (rev 2042)
@@ -1,812 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source
- * Copyright 2009, JBoss Inc., and individual contributors as indicated
- * by the @authors tag. See the copyright.txt 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.net.InetAddress;
-import java.net.Socket;
-import java.net.URLEncoder;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
-
-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.coyote.RequestGroupInfo;
-import org.apache.coyote.RequestInfo;
-import org.apache.tomcat.util.modeler.Registry;
-import org.apache.tomcat.util.net.JIoEndpoint;
-import org.apache.tomcat.util.net.SSLImplementation;
-import org.apache.tomcat.util.net.ServerSocketFactory;
-import org.apache.tomcat.util.net.SocketStatus;
-import org.apache.tomcat.util.net.JIoEndpoint.Handler;
-import org.apache.tomcat.util.res.StringManager;
-
-
-/**
- * Abstract the protocol implementation, including threading, etc.
- * Processor is single threaded and specific to stream-based protocols,
- * will not fit Jk protocols like JNI.
- *
- * @author Remy Maucherat
- * @author Costin Manolache
- */
-public class Http11Protocol
- implements ProtocolHandler, MBeanRegistration {
-
-
- protected static org.jboss.logging.Logger log
- = org.jboss.logging.Logger.getLogger(Http11Protocol.class);
-
- /**
- * The string manager for this package.
- */
- protected static StringManager sm =
- StringManager.getManager(Constants.Package);
-
-
- // ------------------------------------------------------------ Constructor
-
-
- public Http11Protocol() {
- setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
- setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
- //setServerSoTimeout(Constants.DEFAULT_SERVER_SOCKET_TIMEOUT);
- setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
- }
-
-
- // ----------------------------------------------------------------- Fields
-
-
- protected Http11ConnectionHandler cHandler = new Http11ConnectionHandler(this);
- protected JIoEndpoint endpoint = new JIoEndpoint();
-
-
- // *
- protected ObjectName tpOname = null;
- // *
- protected ObjectName rgOname = null;
-
-
- protected ServerSocketFactory socketFactory = null;
- protected SSLImplementation sslImplementation = null;
-
-
- // ----------------------------------------- ProtocolHandler Implementation
- // *
-
-
- protected HashMap<String, Object> attributes = new HashMap<String, Object>();
-
-
- /**
- * Pass config info
- */
- public void setAttribute(String name, Object value) {
- if (log.isTraceEnabled()) {
- log.trace(sm.getString("http11protocol.setattribute", name, value));
- }
- attributes.put(name, value);
- }
-
- public Object getAttribute(String key) {
- return attributes.get(key);
- }
-
- public Iterator getAttributeNames() {
- return attributes.keySet().iterator();
- }
-
- /**
- * Set a property.
- */
- public void setProperty(String name, String value) {
- setAttribute(name, value);
- }
-
- /**
- * Get a property
- */
- public String getProperty(String name) {
- return (String)getAttribute(name);
- }
-
- /**
- * The adapter, used to call the connector.
- */
- protected Adapter adapter;
- public void setAdapter(Adapter adapter) { this.adapter = adapter; }
- public Adapter getAdapter() { return adapter; }
-
-
- public boolean hasIoEvents() {
- return false;
- }
-
- public RequestGroupInfo getRequestGroupInfo() {
- return cHandler.global;
- }
-
- 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(sslImplementationName);
- socketFactory = sslImplementation.getServerSocketFactory();
- endpoint.setServerSocketFactory(socketFactory);
- } else if (socketFactoryName != null) {
- socketFactory = (ServerSocketFactory) Class.forName(socketFactoryName).newInstance();
- endpoint.setServerSocketFactory(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.init();
- } catch (Exception ex) {
- log.error(sm.getString("http11protocol.endpoint.initerror"), ex);
- throw ex;
- }
- if (log.isDebugEnabled())
- log.debug(sm.getString("http11protocol.init", getName()));
-
- }
-
- 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 endpoint");
- }
- 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;
- }
- if (log.isInfoEnabled())
- log.info(sm.getString("http11protocol.start", getName()));
- }
-
- public void pause() throws Exception {
- try {
- endpoint.pause();
- } catch (Exception ex) {
- log.error(sm.getString("http11protocol.endpoint.pauseerror"), ex);
- throw ex;
- }
- // Wait for a while until all the processors are no longer processing requests
- 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) {
- ;
- }
- done = false;
- break;
- }
- }
- }
- if (log.isInfoEnabled())
- log.info(sm.getString("http11protocol.pause", getName()));
- }
-
- public void resume() throws Exception {
- try {
- endpoint.resume();
- } catch (Exception ex) {
- log.error(sm.getString("http11protocol.endpoint.resumeerror"), ex);
- throw ex;
- }
- if (log.isInfoEnabled())
- log.info(sm.getString("http11protocol.resume", getName()));
- }
-
- public void destroy() throws Exception {
- if (log.isInfoEnabled())
- log.info(sm.getString("http11protocol.stop", getName()));
- endpoint.destroy();
- 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);
- }
- }
-
- public String getJmxName() {
- String encodedAddr = "";
- if (getAddress() != null) {
- encodedAddr = "" + getAddress();
- encodedAddr = URLEncoder.encode(encodedAddr.replace('/', '-').replace(':', '_').replace('%', '-')) + "-";
- }
- return ("http-" + encodedAddr + endpoint.getPort());
- }
-
- public String getName() {
- String encodedAddr = "";
- if (getAddress() != null) {
- encodedAddr = getAddress() + ":";
- }
- return ("http-" + encodedAddr + endpoint.getPort());
- }
-
- // ------------------------------------------------------------- Properties
-
-
- /**
- * Processor cache.
- */
- protected int processorCache = -1;
- public int getProcessorCache() { return this.processorCache; }
- public void setProcessorCache(int processorCache) { this.processorCache = processorCache; }
-
- protected int socketBuffer = 9000;
- public int getSocketBuffer() { return socketBuffer; }
- public void setSocketBuffer(int socketBuffer) { this.socketBuffer = socketBuffer; }
-
- /**
- * This field indicates if the protocol is secure from the perspective of
- * the client (= https is used).
- */
- protected boolean secure;
- public boolean getSecure() { return secure; }
- public void setSecure(boolean b) { secure = b; }
-
- protected boolean SSLEnabled = false;
- public boolean isSSLEnabled() { return SSLEnabled;}
- public void setSSLEnabled(boolean SSLEnabled) {this.SSLEnabled = SSLEnabled;}
-
- /**
- * Name of the socket factory.
- */
- protected String socketFactoryName = null;
- public String getSocketFactory() { return socketFactoryName; }
- public void setSocketFactory(String valueS) { socketFactoryName = valueS; }
-
- /**
- * Name of the SSL implementation.
- */
- protected String sslImplementationName=null;
- public String getSSLImplementation() { return sslImplementationName; }
- public void setSSLImplementation( String valueS) {
- sslImplementationName = valueS;
- setSecure(true);
- }
-
-
- // HTTP
- /**
- * 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 = (org.apache.tomcat.util.Constants.LOW_MEMORY) ? 1 :
- Integer.valueOf(System.getProperty("org.apache.coyote.http11.Http11Protocol.MAX_KEEP_ALIVE_REQUESTS", "-1")).intValue();
- public int getMaxKeepAliveRequests() { return maxKeepAliveRequests; }
- public void setMaxKeepAliveRequests(int mkar) { maxKeepAliveRequests = mkar; }
-
- // HTTP
- /**
- * The number of seconds Tomcat will wait for a subsequent request
- * before closing the connection. The default is the same as for
- * Apache HTTP Server (15 000 milliseconds).
- */
- protected int keepAliveTimeout = -1;
- public int getKeepAliveTimeout() { return keepAliveTimeout; }
- public void setKeepAliveTimeout(int timeout) { keepAliveTimeout = timeout; }
-
- // HTTP
- /**
- * 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;
- public int getTimeout() { return timeout; }
- public void setTimeout(int timeout) { this.timeout = timeout; }
-
-
- // *
- /**
- * Maximum size of the post which will be saved when processing certain
- * requests, such as a POST.
- */
- protected int maxSavePostSize = 4 * 1024;
- public int getMaxSavePostSize() { return maxSavePostSize; }
- public void setMaxSavePostSize(int valueI) { maxSavePostSize = valueI; }
-
-
- // HTTP
- /**
- * Maximum size of the HTTP message header.
- */
- protected int maxHttpHeaderSize = Integer.valueOf(System.getProperty("org.apache.coyote.http11.Http11Protocol.MAX_HEADER_SIZE", "8192")).intValue();
- public int getMaxHttpHeaderSize() { return maxHttpHeaderSize; }
- public void setMaxHttpHeaderSize(int valueI) { maxHttpHeaderSize = valueI; }
-
-
- // HTTP
- /**
- * If true, the regular socket timeout will be used for the full duration
- * of the connection.
- */
- protected boolean disableUploadTimeout = true;
- public boolean getDisableUploadTimeout() { return disableUploadTimeout; }
- public void setDisableUploadTimeout(boolean isDisabled) { disableUploadTimeout = isDisabled; }
-
-
- // HTTP
- /**
- * Integrated compression support.
- */
- protected String compression = System.getProperty("org.apache.coyote.http11.Http11Protocol.COMPRESSION", "off");
- public String getCompression() { return compression; }
- public void setCompression(String valueS) { compression = valueS; }
-
-
- // HTTP
- protected String noCompressionUserAgents = System.getProperty("org.apache.coyote.http11.Http11Protocol.COMPRESSION_RESTRICTED_UA");
- public String getNoCompressionUserAgents() { return noCompressionUserAgents; }
- public void setNoCompressionUserAgents(String valueS) { noCompressionUserAgents = valueS; }
-
-
- // HTTP
- protected String compressableMimeTypes = System.getProperty("org.apache.coyote.http11.Http11Protocol.COMPRESSION_MIME_TYPES", "text/html,text/xml,text/plain");
- public String getCompressableMimeType() { return compressableMimeTypes; }
- public void setCompressableMimeType(String valueS) { compressableMimeTypes = valueS; }
-
-
- // HTTP
- protected int compressionMinSize = Integer.valueOf(System.getProperty("org.apache.coyote.http11.Http11Protocol.COMPRESSION_MIN_SIZE", "2048")).intValue();
- public int getCompressionMinSize() { return compressionMinSize; }
- public void setCompressionMinSize(int valueI) { compressionMinSize = valueI; }
-
-
- // HTTP
- /**
- * User agents regular expressions which should be restricted to HTTP/1.0 support.
- */
- protected String restrictedUserAgents = null;
- public String getRestrictedUserAgents() { return restrictedUserAgents; }
- public void setRestrictedUserAgents(String valueS) { restrictedUserAgents = valueS; }
-
- // HTTP
- /**
- * Server header.
- */
- protected String server = System.getProperty("org.apache.coyote.http11.Http11Protocol.SERVER");
- public void setServer( String server ) { this.server = server; }
- public String getServer() { return server; }
-
- public Executor getExecutor() { return endpoint.getExecutor(); }
- public void setExecutor(Executor executor) { endpoint.setExecutor(executor); }
-
- public int getMaxThreads() { return endpoint.getMaxThreads(); }
- public void setMaxThreads(int maxThreads) { endpoint.setMaxThreads(maxThreads); }
-
- public int getThreadPriority() { return endpoint.getThreadPriority(); }
- public void setThreadPriority(int threadPriority) { endpoint.setThreadPriority(threadPriority); }
-
- public int getBacklog() { return endpoint.getBacklog(); }
- public void setBacklog(int backlog) { endpoint.setBacklog(backlog); }
-
- public int getPort() { return endpoint.getPort(); }
- public void setPort(int port) { endpoint.setPort(port); }
-
- public InetAddress getAddress() { return endpoint.getAddress(); }
- public void setAddress(InetAddress ia) { endpoint.setAddress(ia); }
-
- public boolean getTcpNoDelay() { return endpoint.getTcpNoDelay(); }
- public void setTcpNoDelay(boolean tcpNoDelay) { endpoint.setTcpNoDelay(tcpNoDelay); }
-
- public int getSoLinger() { return endpoint.getSoLinger(); }
- public void setSoLinger(int soLinger) { endpoint.setSoLinger(soLinger); }
-
- public int getSoTimeout() { return endpoint.getSoTimeout(); }
- public void setSoTimeout(int soTimeout) { endpoint.setSoTimeout(soTimeout); }
-
- public void setPollerSize(int pollerSize) { endpoint.setPollerSize(pollerSize); }
- public int getPollerSize() { return endpoint.getPollerSize(); }
-
- // HTTP
- /**
- * Return the Keep-Alive policy for the connection.
- */
- public boolean getKeepAlive() {
- return ((maxKeepAliveRequests != 0) && (maxKeepAliveRequests != 1));
- }
-
- // HTTP
- /**
- * Set the keep-alive policy for this connection.
- */
- public void setKeepAlive(boolean keepAlive) {
- if (!keepAlive) {
- setMaxKeepAliveRequests(1);
- }
- }
-
- /*
- * Note: All the following are JSSE/java.io specific attributes.
- */
-
- public String getKeystore() {
- return (String) getAttribute("keystore");
- }
-
- public void setKeystore( String k ) {
- setAttribute("keystore", k);
- }
-
- public String getKeypass() {
- return (String) getAttribute("keypass");
- }
-
- public void setKeypass( String k ) {
- attributes.put("keypass", k);
- //setAttribute("keypass", k);
- }
-
- public String getKeytype() {
- return (String) getAttribute("keystoreType");
- }
-
- public void setKeytype( String k ) {
- setAttribute("keystoreType", k);
- }
-
- public String getClientauth() {
- return (String) getAttribute("clientauth");
- }
-
- public void setClientauth( String k ) {
- setAttribute("clientauth", k);
- }
-
- public String getProtocols() {
- return (String) getAttribute("protocols");
- }
-
- public void setProtocols(String k) {
- setAttribute("protocols", k);
- }
-
- public String getAlgorithm() {
- return (String) getAttribute("algorithm");
- }
-
- public void setAlgorithm( String k ) {
- setAttribute("algorithm", k);
- }
-
- public String getCiphers() {
- return (String) getAttribute("ciphers");
- }
-
- public void setCiphers(String ciphers) {
- setAttribute("ciphers", ciphers);
- }
-
- public String getKeyAlias() {
- return (String) getAttribute("keyAlias");
- }
-
- public void setKeyAlias(String keyAlias) {
- setAttribute("keyAlias", keyAlias);
- }
-
- public SSLContext getSSLContext() {
- return (SSLContext) getAttribute("SSLContext");
- }
-
- public void setSSLContext(SSLContext sslContext) {
- setAttribute("SSLContext", sslContext);
- }
-
- // ----------------------------------- Http11ConnectionHandler Inner Class
-
- protected static class Http11ConnectionHandler implements Handler {
-
- protected Http11Protocol proto;
- protected AtomicLong registerCount = new AtomicLong(0);
- protected RequestGroupInfo global = new RequestGroupInfo();
-
- protected ConcurrentHashMap<Socket, Http11Processor> connections =
- new ConcurrentHashMap<Socket, Http11Processor>();
- protected ConcurrentLinkedQueue<Http11Processor> recycledProcessors =
- new ConcurrentLinkedQueue<Http11Processor>() {
- protected AtomicInteger size = new AtomicInteger(0);
- public boolean offer(Http11Processor 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;
- }
-
- public Http11Processor poll() {
- Http11Processor result = super.poll();
- if ( result != null ) {
- size.decrementAndGet();
- }
- return result;
- }
-
- public void clear() {
- Http11Processor next = poll();
- while ( next != null ) {
- unregister(next);
- next = poll();
- }
- super.clear();
- size.set(0);
- }
- };
-
- Http11ConnectionHandler(Http11Protocol proto) {
- this.proto = proto;
- }
-
- public SocketState event(Socket socket, SocketStatus status) {
- Http11Processor result = connections.get(socket);
- SocketState state = SocketState.CLOSED;
- if (result != null) {
- result.startProcessing();
- // Call the appropriate event
- try {
- state = result.event(status);
- } catch (java.net.SocketException e) {
- // SocketExceptions are normal
- Http11Protocol.log.debug
- (sm.getString
- ("http11protocol.proto.socketexception.debug"), e);
- } catch (java.io.IOException e) {
- // IOExceptions are normal
- Http11Protocol.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.
- Http11Protocol.log.error
- (sm.getString("http11protocol.proto.error"), e);
- } finally {
- if (state != SocketState.LONG) {
- connections.remove(socket);
- recycledProcessors.offer(result);
- } else {
- if (proto.endpoint.isRunning()) {
- proto.endpoint.getEventPoller().add(socket, result.getTimeout(),
- result.getResumeNotification(), false);
- }
- }
- result.endProcessing();
- }
- }
- return state;
- }
-
- public SocketState process(Socket socket) {
- Http11Processor processor = recycledProcessors.poll();
- try {
-
- if (processor == null) {
- processor = createProcessor();
- }
-
- if (proto.secure && (proto.sslImplementation != null)) {
- processor.setSSLSupport
- (proto.sslImplementation.getSSLSupport(socket));
- } else {
- processor.setSSLSupport(null);
- }
-
- SocketState state = processor.process(socket);
- 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(socket, processor);
- proto.endpoint.getEventPoller().add(socket, processor.getTimeout(),
- processor.getResumeNotification(), false);
- } else {
- recycledProcessors.offer(processor);
- }
- return state;
-
- } catch(java.net.SocketException e) {
- // SocketExceptions are normal
- Http11Protocol.log.debug
- (sm.getString
- ("http11protocol.proto.socketexception.debug"), e);
- } catch (java.io.IOException e) {
- // IOExceptions are normal
- Http11Protocol.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.
- Http11Protocol.log.error
- (sm.getString("http11protocol.proto.error"), e);
- }
- recycledProcessors.offer(processor);
- return SocketState.CLOSED;
- }
-
- protected Http11Processor createProcessor() {
- Http11Processor processor =
- new Http11Processor(proto.maxHttpHeaderSize, proto.endpoint);
- processor.setAdapter(proto.adapter);
- processor.setMaxKeepAliveRequests(proto.maxKeepAliveRequests);
- processor.setKeepAliveTimeout(proto.keepAliveTimeout);
- 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.setSocketBuffer(proto.socketBuffer);
- processor.setMaxSavePostSize(proto.maxSavePostSize);
- processor.setServer(proto.server);
- register(processor);
- return processor;
- }
-
- protected void register(Http11Processor 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.getJmxName() + ",name=HttpRequest" + count);
- if (log.isDebugEnabled()) {
- log.debug("Register " + rpName);
- }
- Registry.getRegistry(null, null).registerComponent(rp, rpName, null);
- rp.setRpName(rpName);
- } catch (Exception e) {
- log.warn("Error registering request");
- }
- }
- }
- }
-
- protected void unregister(Http11Processor 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) {
- log.warn("Error unregistering request", e);
- }
- }
- }
- }
-
- }
-
-
- // -------------------- JMX related methods --------------------
-
- // *
- protected String domain;
- protected ObjectName oname;
- protected MBeanServer mserver;
-
- public ObjectName getObjectName() {
- return oname;
- }
-
- public String getDomain() {
- return domain;
- }
-
- public ObjectName preRegister(MBeanServer server,
- ObjectName name) throws Exception {
- oname=name;
- mserver=server;
- domain=name.getDomain();
- return name;
- }
-
- public void postRegister(Boolean registrationDone) {
- }
-
- public void preDeregister() throws Exception {
- }
-
- public void postDeregister() {
- }
-}
Deleted: trunk/java/org/apache/coyote/http11/InternalInputBuffer.java
===================================================================
--- trunk/java/org/apache/coyote/http11/InternalInputBuffer.java 2012-06-13 08:50:17 UTC (rev 2041)
+++ trunk/java/org/apache/coyote/http11/InternalInputBuffer.java 2012-06-14 14:11:34 UTC (rev 2042)
@@ -1,793 +0,0 @@
-/*
- * 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.InputStream;
-import java.io.EOFException;
-
-import org.apache.tomcat.util.buf.ByteChunk;
-import org.apache.tomcat.util.buf.MessageBytes;
-import org.apache.tomcat.util.http.MimeHeaders;
-import org.apache.tomcat.util.res.StringManager;
-
-import org.apache.coyote.InputBuffer;
-import org.apache.coyote.Request;
-
-/**
- * Implementation of InputBuffer which provides HTTP request header parsing as
- * well as transfer decoding.
- *
- * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
- */
-public class InternalInputBuffer implements InputBuffer {
-
-
- // -------------------------------------------------------------- Constants
-
-
- // ----------------------------------------------------------- Constructors
-
-
- /**
- * Default constructor.
- */
- public InternalInputBuffer(Request request) {
- this(request, Constants.DEFAULT_HTTP_HEADER_BUFFER_SIZE);
- }
-
-
- /**
- * Alternate constructor.
- */
- public InternalInputBuffer(Request request, int headerBufferSize) {
-
- this.request = request;
- headers = request.getMimeHeaders();
-
- buf = new byte[headerBufferSize];
-
- inputStreamInputBuffer = new InputStreamInputBuffer();
-
- filterLibrary = new InputFilter[0];
- activeFilters = new InputFilter[0];
- lastActiveFilter = -1;
-
- parsingHeader = true;
- swallowInput = true;
-
- }
-
-
- // -------------------------------------------------------------- Variables
-
-
- /**
- * The string manager for this package.
- */
- protected static StringManager sm =
- StringManager.getManager(Constants.Package);
-
-
- // ----------------------------------------------------- Instance Variables
-
-
- /**
- * 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;
-
-
- /**
- * Last valid byte.
- */
- protected int lastValid;
-
-
- /**
- * Position in the buffer.
- */
- protected int pos;
-
-
- /**
- * Pos of the end of the header in the buffer, which is also the
- * start of the body.
- */
- protected int end;
-
-
- /**
- * Underlying input stream.
- */
- protected InputStream inputStream;
-
-
- /**
- * Underlying input buffer.
- */
- protected InputBuffer inputStreamInputBuffer;
-
-
- /**
- * 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;
-
-
- // ------------------------------------------------------------- Properties
-
-
- /**
- * Set the underlying socket input stream.
- */
- public void setInputStream(InputStream inputStream) {
-
- // FIXME: Check for null ?
-
- this.inputStream = inputStream;
-
- }
-
-
- /**
- * Get the underlying socket input stream.
- */
- public InputStream getInputStream() {
-
- return inputStream;
-
- }
-
-
- /**
- * Add an input filter to the filter library.
- */
- public void addFilter(InputFilter filter) {
-
- // FIXME: Check for null ?
-
- 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];
-
- }
-
-
- /**
- * Get filters.
- */
- public InputFilter[] getFilters() {
-
- return filterLibrary;
-
- }
-
-
- /**
- * Clear filters.
- */
- public void clearFilters() {
-
- filterLibrary = new InputFilter[0];
- lastActiveFilter = -1;
-
- }
-
-
- /**
- * Add an input filter to the filter library.
- */
- public void addActiveFilter(InputFilter filter) {
-
- if (lastActiveFilter == -1) {
- filter.setBuffer(inputStreamInputBuffer);
- } else {
- for (int i = 0; i <= lastActiveFilter; i++) {
- if (activeFilters[i] == filter)
- return;
- }
- filter.setBuffer(activeFilters[lastActiveFilter]);
- }
-
- activeFilters[++lastActiveFilter] = filter;
-
- filter.setRequest(request);
-
- }
-
-
- /**
- * Set the swallow input flag.
- */
- public void setSwallowInput(boolean swallowInput) {
- this.swallowInput = swallowInput;
- }
-
-
- // --------------------------------------------------------- Public Methods
-
-
- /**
- * Recycle the input buffer. This should be called when closing the
- * connection.
- */
- public void recycle() {
-
- // Recycle Request object
- request.recycle();
-
- inputStream = null;
- lastValid = 0;
- 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.
- */
- 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;
- pos = 0;
- lastActiveFilter = -1;
- parsingHeader = true;
- swallowInput = true;
-
- 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.
- */
- 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
- */
- 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;
-
- }
-
-
- // ---------------------------------------------------- InputBuffer Methods
-
-
- /**
- * Read some bytes.
- */
- public int doRead(ByteChunk chunk, Request req)
- throws IOException {
-
- if (lastActiveFilter == -1)
- return inputStreamInputBuffer.doRead(chunk, req);
- else
- return activeFilters[lastActiveFilter].doRead(chunk,req);
-
- }
-
-
- // ------------------------------------------------------ Protected Methods
-
-
- /**
- * Fill the internal buffer using data from the undelying input stream.
- *
- * @return false if at end of stream
- */
- protected boolean fill()
- throws IOException {
-
- int nRead = 0;
-
- if (parsingHeader) {
-
- if (lastValid == buf.length) {
- throw new IllegalArgumentException
- (sm.getString("iib.requestheadertoolarge.error"));
- }
-
- nRead = inputStream.read(buf, pos, buf.length - lastValid);
- if (nRead > 0) {
- lastValid = pos + nRead;
- }
-
- } 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;
- nRead = inputStream.read(buf, pos, buf.length - lastValid);
- if (nRead > 0) {
- lastValid = pos + nRead;
- }
-
- }
-
- return (nRead > 0);
-
- }
-
-
- // ------------------------------------- InputStreamInputBuffer Inner Class
-
-
- /**
- * This class is an input buffer which will read its data from an input
- * stream.
- */
- protected class InputStreamInputBuffer
- 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);
-
- }
-
-
- }
-
-
-}
Deleted: trunk/java/org/apache/coyote/http11/InternalOutputBuffer.java
===================================================================
--- trunk/java/org/apache/coyote/http11/InternalOutputBuffer.java 2012-06-13 08:50:17 UTC (rev 2041)
+++ trunk/java/org/apache/coyote/http11/InternalOutputBuffer.java 2012-06-14 14:11:34 UTC (rev 2042)
@@ -1,762 +0,0 @@
-/*
- * 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.OutputStream;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-
-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.apache.coyote.ActionCode;
-import org.apache.coyote.OutputBuffer;
-import org.apache.coyote.Response;
-
-/**
- * Output buffer.
- *
- * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
- */
-public class InternalOutputBuffer
- implements OutputBuffer, ByteChunk.ByteOutputChannel {
-
- // -------------------------------------------------------------- Constants
-
-
- // ----------------------------------------------------------- Constructors
-
-
- /**
- * Default constructor.
- */
- public InternalOutputBuffer(Response response) {
- this(response, Constants.DEFAULT_HTTP_HEADER_BUFFER_SIZE);
- }
-
-
- /**
- * Alternate constructor.
- */
- public InternalOutputBuffer(Response response, int headerBufferSize) {
-
- this.response = response;
-
- headers = response.getMimeHeaders();
-
- buf = new byte[headerBufferSize];
-
- outputStreamOutputBuffer = new OutputStreamOutputBuffer();
-
- filterLibrary = new OutputFilter[0];
- activeFilters = new OutputFilter[0];
- lastActiveFilter = -1;
-
- socketBuffer = new ByteChunk();
- socketBuffer.setByteOutputChannel(this);
-
- committed = false;
- finished = false;
-
- }
-
-
- // -------------------------------------------------------------- Variables
-
-
- /**
- * The string manager for this package.
- */
- protected static StringManager sm =
- StringManager.getManager(Constants.Package);
-
-
- // ----------------------------------------------------- Instance Variables
-
-
- /**
- * Associated Coyote response.
- */
- protected Response response;
-
-
- /**
- * Headers of the associated request.
- */
- protected MimeHeaders headers;
-
-
- /**
- * Committed flag.
- */
- protected boolean committed;
-
-
- /**
- * Finished flag.
- */
- protected boolean finished;
-
-
- /**
- * The buffer used for header composition.
- */
- protected byte[] buf;
-
-
- /**
- * Position in the buffer.
- */
- protected int pos;
-
-
- /**
- * Underlying output stream.
- */
- protected OutputStream outputStream;
-
-
- /**
- * Underlying output buffer.
- */
- protected OutputBuffer outputStreamOutputBuffer;
-
-
- /**
- * 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;
-
-
- /**
- * Socket buffer.
- */
- protected ByteChunk socketBuffer;
-
-
- /**
- * Socket buffer (extra buffering to reduce number of packets sent).
- */
- protected boolean useSocketBuffer = false;
-
-
- // ------------------------------------------------------------- Properties
-
-
- /**
- * Set the underlying socket output stream.
- */
- public void setOutputStream(OutputStream outputStream) {
-
- // FIXME: Check for null ?
-
- this.outputStream = outputStream;
-
- }
-
-
- /**
- * Get the underlying socket output stream.
- */
- public OutputStream getOutputStream() {
-
- return outputStream;
-
- }
-
-
- /**
- * Set the socket buffer size.
- */
- public void setSocketBuffer(int socketBufferSize) {
-
- if (socketBufferSize > 500) {
- useSocketBuffer = true;
- socketBuffer.allocate(socketBufferSize, socketBufferSize);
- } else {
- useSocketBuffer = false;
- }
-
- }
-
-
- /**
- * Add an output filter to the filter library.
- */
- 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.
- */
- public OutputFilter[] getFilters() {
-
- return filterLibrary;
-
- }
-
-
- /**
- * Clear filters.
- */
- public void clearFilters() {
-
- filterLibrary = new OutputFilter[0];
- lastActiveFilter = -1;
-
- }
-
-
- /**
- * Add an output filter to the filter library.
- */
- public void addActiveFilter(OutputFilter filter) {
-
- if (lastActiveFilter == -1) {
- filter.setBuffer(outputStreamOutputBuffer);
- } else {
- for (int i = 0; i <= lastActiveFilter; i++) {
- if (activeFilters[i] == filter)
- return;
- }
- filter.setBuffer(activeFilters[lastActiveFilter]);
- }
-
- activeFilters[++lastActiveFilter] = filter;
-
- filter.setResponse(response);
-
- }
-
-
- // --------------------------------------------------------- Public Methods
-
-
- /**
- * 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
- if (useSocketBuffer) {
- socketBuffer.flushBuffer();
- }
-
- }
-
-
- /**
- * Recycle the output buffer. This should be called when closing the
- * connection.
- */
- public void recycle() {
-
- // Recycle Request object
- response.recycle();
- socketBuffer.recycle();
-
- outputStream = null;
- 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();
- socketBuffer.recycle();
-
- // Recycle filters
- for (int i = 0; i <= lastActiveFilter; i++) {
- activeFilters[i].recycle();
- }
-
- // Reset pointers
- pos = 0;
- lastActiveFilter = -1;
- committed = false;
- finished = 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();
-
- if (useSocketBuffer) {
- socketBuffer.flushBuffer();
- }
-
- finished = true;
-
- }
-
-
- // ------------------------------------------------ HTTP/1.1 Output Methods
-
-
- /**
- * Send an acknoledgement.
- */
- public void sendAck()
- throws IOException {
-
- if (!committed)
- outputStream.write(Constants.ACK_BYTES);
-
- }
-
-
- /**
- * 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(getMessage(status));
- } else {
- write(message.replace('\n', ' ').replace('\r', ' '));
- }
-
- // End the response status line
- if (org.apache.coyote.Constants.IS_SECURITY_ENABLED){
- AccessController.doPrivileged(
- new PrivilegedAction(){
- public Object run(){
- buf[pos++] = Constants.CR;
- buf[pos++] = Constants.LF;
- return null;
- }
- }
- );
- } else {
- buf[pos++] = Constants.CR;
- buf[pos++] = Constants.LF;
- }
-
- }
-
- private String getMessage(final int message){
- if (org.apache.coyote.Constants.IS_SECURITY_ENABLED){
- return (String)AccessController.doPrivileged(
- new PrivilegedAction(){
- public Object run(){
- return HttpMessages.getMessage(message);
- }
- }
- );
- } else {
- return HttpMessages.getMessage(message);
- }
- }
-
- /**
- * 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;
-
- }
-
-
- // --------------------------------------------------- OutputBuffer Methods
-
-
- /**
- * 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 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 (lastActiveFilter == -1)
- return outputStreamOutputBuffer.doWrite(chunk, res);
- else
- return activeFilters[lastActiveFilter].doWrite(chunk, res);
-
- }
-
-
- // ------------------------------------------------------ Protected Methods
-
-
- /**
- * 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
- if (useSocketBuffer) {
- socketBuffer.append(buf, 0, pos);
- } else {
- outputStream.write(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.getType() == MessageBytes.T_BYTES) {
- ByteChunk bc = mb.getByteChunk();
- write(bc);
- } else if (mb.getType() == MessageBytes.T_CHARS) {
- CharChunk cc = mb.getCharChunk();
- write(cc);
- } else {
- write(mb.toString());
- }
-
- }
-
-
- /**
- * 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 = ' ';
- } else if (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.
- */
- public void realWriteBytes(byte cbuf[], int off, int len)
- throws IOException {
- if (len > 0) {
- outputStream.write(cbuf, off, len);
- }
- }
-
-
- // ----------------------------------- OutputStreamOutputBuffer Inner Class
-
-
- /**
- * This class is an output buffer which will write data to an output
- * stream.
- */
- protected class OutputStreamOutputBuffer
- implements OutputBuffer {
-
-
- /**
- * Write chunk.
- */
- public int doWrite(ByteChunk chunk, Response res)
- throws IOException {
-
- int length = chunk.getLength();
- if (useSocketBuffer) {
- socketBuffer.append(chunk.getBuffer(), chunk.getStart(),
- length);
- } else {
- outputStream.write(chunk.getBuffer(), chunk.getStart(),
- length);
- }
- return length;
-
- }
-
-
- }
-
-
-}
Deleted: trunk/java/org/apache/tomcat/util/net/DefaultServerSocketFactory.java
===================================================================
--- trunk/java/org/apache/tomcat/util/net/DefaultServerSocketFactory.java 2012-06-13 08:50:17 UTC (rev 2041)
+++ trunk/java/org/apache/tomcat/util/net/DefaultServerSocketFactory.java 2012-06-14 14:11:34 UTC (rev 2042)
@@ -1,70 +0,0 @@
-/*
- * 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;
-
-import java.io.*;
-import java.net.*;
-
-/**
- * Default server socket factory. Doesn't do much except give us
- * plain ol' server sockets.
- *
- * @author db(a)eng.sun.com
- * @author Harish Prabandham
- */
-
-// Default implementation of server sockets.
-
-//
-// WARNING: Some of the APIs in this class are used by J2EE.
-// Please talk to harishp(a)eng.sun.com before making any changes.
-//
-class DefaultServerSocketFactory extends ServerSocketFactory {
-
- DefaultServerSocketFactory () {
- /* NOTHING */
- }
-
- public ServerSocket createSocket (int port)
- throws IOException {
- return new ServerSocket (port);
- }
-
- public ServerSocket createSocket (int port, int backlog)
- throws IOException {
- return new ServerSocket (port, backlog);
- }
-
- public ServerSocket createSocket (int port, int backlog,
- InetAddress ifAddress)
- throws IOException {
- return new ServerSocket (port, backlog, ifAddress);
- }
-
- public Socket acceptSocket(ServerSocket socket)
- throws IOException {
- return socket.accept();
- }
-
- public void handshake(Socket sock)
- throws IOException {
- ; // NOOP
- }
-
-
- }
Deleted: trunk/java/org/apache/tomcat/util/net/JIoEndpoint.java
===================================================================
--- trunk/java/org/apache/tomcat/util/net/JIoEndpoint.java 2012-06-13 08:50:17 UTC (rev 2041)
+++ trunk/java/org/apache/tomcat/util/net/JIoEndpoint.java 2012-06-14 14:11:34 UTC (rev 2042)
@@ -1,1328 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source
- * Copyright 2009, JBoss Inc., and individual contributors as indicated
- * by the @authors tag. See the copyright.txt 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.BindException;
-import java.net.InetAddress;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.util.concurrent.Executor;
-
-import org.apache.tomcat.util.res.StringManager;
-import org.jboss.logging.Logger;
-
-/**
- * Handle incoming TCP connections.
- *
- * This class implement a simple server model: one listener thread accepts on a socket and
- * creates a new worker thread for each incoming connection.
- *
- * More advanced Endpoints will reuse the threads, use queues, etc.
- *
- * @author James Duncan Davidson
- * @author Jason Hunter
- * @author James Todd
- * @author Costin Manolache
- * @author Gal Shachor
- * @author Yoav Shapira
- * @author Remy Maucherat
- */
-public class JIoEndpoint {
-
-
- // -------------------------------------------------------------- Constants
-
-
- protected static Logger log = Logger.getLogger(JIoEndpoint.class);
-
- protected StringManager sm =
- StringManager.getManager("org.apache.tomcat.util.net.res");
-
-
- // ----------------------------------------------------------------- Fields
-
-
- /**
- * Available workers.
- */
- protected WorkerStack workers = null;
-
-
- /**
- * 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;
-
-
- /**
- * 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;
-
-
- /**
- * Associated server socket.
- */
- protected ServerSocket serverSocket = null;
-
-
- // ------------------------------------------------------------- Properties
-
-
- /**
- * Acceptor thread count.
- */
- protected int acceptorThreadCount = 0;
- public void setAcceptorThreadCount(int acceptorThreadCount) { this.acceptorThreadCount = acceptorThreadCount; }
- public int getAcceptorThreadCount() { return acceptorThreadCount; }
-
-
- /**
- * External Executor based thread pool.
- */
- protected Executor executor = null;
- public void setExecutor(Executor executor) { this.executor = executor; }
- public Executor getExecutor() { return executor; }
-
-
- /**
- * Maximum amount of worker threads.
- */
- protected int maxThreads = (org.apache.tomcat.util.Constants.LOW_MEMORY) ? 64 : ((Constants.MAX_THREADS == -1) ? 512 * Runtime.getRuntime().availableProcessors() : Constants.MAX_THREADS);
- public void setMaxThreads(int maxThreads) { this.maxThreads = maxThreads; }
- public int getMaxThreads() { return maxThreads; }
-
-
- /**
- * Priority of the acceptor and poller threads.
- */
- protected int threadPriority = Thread.NORM_PRIORITY;
- public void setThreadPriority(int threadPriority) { this.threadPriority = threadPriority; }
- public int getThreadPriority() { return threadPriority; }
-
-
- /**
- * Size of the socket poller.
- */
- protected int pollerSize = (org.apache.tomcat.util.Constants.LOW_MEMORY) ? 128 : (32 * 1024);
- public void setPollerSize(int pollerSize) { this.pollerSize = pollerSize; }
- public int getPollerSize() { return pollerSize; }
-
-
- /**
- * Keep-Alive timeout.
- */
- protected int keepAliveTimeout = -1;
- public int getKeepAliveTimeout() { return keepAliveTimeout; }
- public void setKeepAliveTimeout(int keepAliveTimeout) { this.keepAliveTimeout = keepAliveTimeout; }
-
-
- /**
- * Server socket port.
- */
- protected int port;
- public int getPort() { return port; }
- public void setPort(int port ) { this.port=port; }
-
-
- /**
- * Address for the server socket.
- */
- protected InetAddress address;
- public InetAddress getAddress() { return address; }
- public void setAddress(InetAddress address) { this.address = address; }
-
-
- /**
- * Handling of accepted sockets.
- */
- protected Handler handler = null;
- public void setHandler(Handler handler ) { this.handler = handler; }
- public Handler getHandler() { return handler; }
-
-
- /**
- * 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;
- public void setBacklog(int backlog) { if (backlog > 0) this.backlog = backlog; }
- public int getBacklog() { return backlog; }
-
-
- /**
- * Socket TCP no delay.
- */
- protected boolean tcpNoDelay = false;
- public boolean getTcpNoDelay() { return tcpNoDelay; }
- public void setTcpNoDelay(boolean tcpNoDelay) { this.tcpNoDelay = tcpNoDelay; }
-
-
- /**
- * Socket linger.
- */
- protected int soLinger = 100;
- public int getSoLinger() { return soLinger; }
- public void setSoLinger(int soLinger) { this.soLinger = soLinger; }
-
-
- /**
- * Socket timeout.
- */
- protected int soTimeout = -1;
- public int getSoTimeout() { return soTimeout; }
- public void setSoTimeout(int soTimeout) { this.soTimeout = soTimeout; }
-
-
- /**
- * 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;
- public void setDaemon(boolean b) { daemon = b; }
- public boolean getDaemon() { return daemon; }
-
-
- /**
- * Name of the thread pool, which will be used for naming child threads.
- */
- protected String name = "TP";
- public void setName(String name) { this.name = name; }
- public String getName() { return name; }
-
-
- /**
- * Server socket factory.
- */
- protected ServerSocketFactory serverSocketFactory = null;
- public void setServerSocketFactory(ServerSocketFactory factory) { this.serverSocketFactory = factory; }
- public ServerSocketFactory getServerSocketFactory() { return serverSocketFactory; }
-
-
- /**
- * The socket poller used for event support.
- */
- protected Poller eventPoller = null;
- public Poller getEventPoller() {
- return eventPoller;
- }
-
-
- public boolean isRunning() {
- return running;
- }
-
- public boolean isPaused() {
- return paused;
- }
-
- public int getCurrentThreadCount() {
- return curThreads;
- }
-
- public int getCurrentThreadsBusy() {
- return workers!=null?curThreads - workers.size():0;
- }
-
-
- // ------------------------------------------------ Handler Inner Interface
-
-
- /**
- * 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.
- */
- public interface Handler {
- public enum SocketState {
- OPEN, CLOSED, LONG
- }
- public SocketState process(Socket socket);
- public SocketState event(Socket socket, SocketStatus status);
- }
-
-
- // --------------------------------------------------- Acceptor Inner Class
-
-
- /**
- * Server socket acceptor thread.
- */
- 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 endpoint is paused
- while (paused) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- // Ignore
- }
- }
-
- // Accept the next incoming connection from the server socket
- try {
- Socket socket = serverSocketFactory.acceptSocket(serverSocket);
- serverSocketFactory.initSocket(socket);
- // Hand this socket off to an appropriate processor
- if (!processSocket(socket)) {
- // Close socket right away
- try { socket.close(); } catch (IOException e) { }
- }
- }catch ( IOException x ) {
- if ( running ) log.error(sm.getString("endpoint.accept.fail"), x);
- } catch (Throwable t) {
- log.error(sm.getString("endpoint.accept.fail"), t);
- }
-
- // The processor will recycle itself when it finishes
-
- }
-
- }
-
- }
-
-
- // ------------------------------------------------- SocketInfo Inner Class
-
-
- /**
- * Socket list class, used to avoid using a possibly large amount of objects
- * with very little actual use.
- */
- public static class SocketInfo {
- public static final int RESUME = 4;
- public static final int WAKEUP = 8;
- public Socket socket;
- public int timeout;
- public int flags;
- public boolean resume() {
- return (flags & RESUME) == RESUME;
- }
- public boolean wakeup() {
- return (flags & WAKEUP) == WAKEUP;
- }
- public static int merge(int flag1, int flag2) {
- return ((flag1 & RESUME) | (flag2 & RESUME))
- | ((flag1 & WAKEUP) & (flag2 & WAKEUP));
- }
- }
-
-
- // --------------------------------------------- SocketTimeouts Inner Class
-
-
- /**
- * Socket list class, used to avoid using a possibly large amount of objects
- * with very little actual use.
- */
- public class SocketTimeouts {
- protected int size;
-
- protected Socket[] sockets;
- protected long[] timeouts;
- protected int pos = 0;
-
- public SocketTimeouts(int size) {
- this.size = 0;
- sockets = new Socket[size];
- timeouts = new long[size];
- }
-
- public void add(Socket socket, long timeout) {
- sockets[size] = socket;
- timeouts[size] = timeout;
- size++;
- }
-
- public boolean remove(Socket socket) {
- for (int i = 0; i < size; i++) {
- if (sockets[i] == socket) {
- sockets[i] = sockets[size - 1];
- timeouts[i] = timeouts[size - 1];
- size--;
- return true;
- }
- }
- return false;
- }
-
- public Socket check(long date) {
- while (pos < size) {
- if (date >= timeouts[pos]) {
- Socket result = sockets[pos];
- sockets[pos] = sockets[size - 1];
- timeouts[pos] = timeouts[size - 1];
- size--;
- return result;
- }
- pos++;
- }
- pos = 0;
- return null;
- }
-
- }
-
-
- // ------------------------------------------------- SocketList Inner Class
-
-
- /**
- * Socket list class, used to avoid using a possibly large amount of objects
- * with very little actual use.
- */
- public class SocketList {
- protected int size;
- protected int pos;
-
- protected Socket[] sockets;
- protected int[] timeouts;
- protected int[] flags;
-
- protected SocketInfo info = new SocketInfo();
-
- public SocketList(int size) {
- this.size = 0;
- pos = 0;
- sockets = new Socket[size];
- timeouts = new int[size];
- flags = new int[size];
- }
-
- public int size() {
- return this.size;
- }
-
- public SocketInfo get() {
- if (pos == size) {
- return null;
- } else {
- info.socket = sockets[pos];
- info.timeout = timeouts[pos];
- info.flags = flags[pos];
- pos++;
- return info;
- }
- }
-
- public void clear() {
- size = 0;
- pos = 0;
- }
-
- public boolean add(Socket socket, int timeout, int flag) {
- if (size == sockets.length) {
- return false;
- } else {
- for (int i = 0; i < size; i++) {
- if (sockets[i] == socket) {
- flags[i] = SocketInfo.merge(flags[i], flag);
- return true;
- }
- }
- sockets[size] = socket;
- timeouts[size] = timeout;
- flags[size] = flag;
- size++;
- return true;
- }
- }
-
- public void duplicate(SocketList copy) {
- copy.size = size;
- copy.pos = pos;
- System.arraycopy(sockets, 0, copy.sockets, 0, size);
- System.arraycopy(timeouts, 0, copy.timeouts, 0, size);
- System.arraycopy(flags, 0, copy.flags, 0, size);
- }
-
- }
-
-
- // ------------------------------------------- SocketProcessor Inner Class
-
-
- /**
- * This class is the equivalent of the Worker, but will simply use in an
- * external Executor thread pool.
- */
- protected class SocketProcessor implements Runnable {
-
- protected Socket socket = null;
-
- public SocketProcessor(Socket socket) {
- this.socket = socket;
- }
-
- public void run() {
-
- // Process the request from this socket
- if (!setSocketOptions(socket) || (handler.process(socket) == Handler.SocketState.CLOSED)) {
- // Close socket
- try { socket.close(); } catch (IOException e) { }
- }
-
- // Finish up this request
- socket = null;
-
- }
-
- }
-
-
- // --------------------------------------- SocketEventProcessor Inner Class
-
-
- /**
- * This class is the equivalent of the Worker, but will simply use in an
- * external Executor thread pool.
- */
- protected class SocketEventProcessor implements Runnable {
-
- protected Socket socket = null;
- protected SocketStatus status = null;
-
- public SocketEventProcessor(Socket socket, SocketStatus status) {
- this.socket = socket;
- this.status = status;
- }
-
- public void run() {
-
- Handler.SocketState socketState = handler.event(socket, status);
- if (socketState == Handler.SocketState.CLOSED) {
- // Close socket
- try { socket.close(); } catch (IOException e) { }
- } else if (socketState == Handler.SocketState.OPEN) {
- // Process the keepalive after the event processing
- // This is the main behavior difference with endpoint with pollers, which
- // will add the socket to the poller
- if (handler.process(socket) == Handler.SocketState.CLOSED) {
- // Close socket
- try { socket.close(); } catch (IOException e) { }
- }
- }
- socket = null;
-
- }
-
- }
-
-
- // ----------------------------------------------------- Poller Inner Class
-
-
- /**
- * Poller class.
- */
- public class Poller implements Runnable {
-
- /**
- * List of sockets to be added to the poller.
- */
- protected SocketList addList = null;
-
- /**
- * List of sockets to be added to the poller.
- */
- protected SocketList localAddList = null;
-
- /**
- * Structure used for storing timeouts.
- */
- protected SocketTimeouts timeouts = null;
-
-
- /**
- * Last run of maintain. Maintain will run usually every 5s.
- */
- protected long lastMaintain = System.currentTimeMillis();
-
-
- /**
- * Amount of connections inside this poller.
- */
- protected int connectionCount = 0;
- public int getConnectionCount() { return connectionCount; }
-
- public Poller() {
- }
-
- /**
- * Create the poller. The java.io poller only deals with timeouts.
- */
- protected void init() {
-
- timeouts = new SocketTimeouts(pollerSize);
-
- connectionCount = 0;
- addList = new SocketList(pollerSize);
- localAddList = new SocketList(pollerSize);
-
- }
-
- /**
- * Destroy the poller.
- */
- protected void destroy() {
- // Wait for pollerTime before doing anything, so that the poller threads
- // exit, otherwise parallel destruction of sockets which are still
- // in the poller can cause problems
- try {
- synchronized (this) {
- this.wait(2);
- }
- } catch (InterruptedException e) {
- // Ignore
- }
- // Close all sockets in the add queue
- SocketInfo info = addList.get();
- while (info != null) {
- if (!processSocket(info.socket, SocketStatus.STOP)) {
- try { info.socket.close(); } catch (IOException e) { }
- }
- info = addList.get();
- }
- addList.clear();
- // Close all sockets still in the poller
- long future = System.currentTimeMillis() + Integer.MAX_VALUE;
- Socket socket = timeouts.check(future);
- while (socket != null) {
- if (!processSocket(socket, SocketStatus.TIMEOUT)) {
- try { socket.close(); } catch (IOException e) { }
- }
- socket = timeouts.check(future);
- }
- connectionCount = 0;
- }
-
- /**
- * Add specified socket and associated pool to the poller. The socket 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).
- *
- * @param socket to add to the poller
- */
- public void add(Socket socket, int timeout, boolean resume, boolean wakeup) {
- if (timeout < 0) {
- timeout = keepAliveTimeout;
- }
- if (timeout < 0) {
- timeout = soTimeout;
- }
- if (timeout <= 0) {
- // Always put a timeout in
- timeout = Integer.MAX_VALUE;
- }
- boolean ok = false;
- synchronized (this) {
- // Add socket to the list. Newly added sockets will wait
- // at most for pollTime before being polled
- if (addList.add(socket, timeout, (resume ? SocketInfo.RESUME : 0)
- | (wakeup ? SocketInfo.WAKEUP : 0))) {
- ok = true;
- this.notify();
- }
- }
- if (!ok) {
- // Can't do anything: close the socket right away
- if (!processSocket(socket, SocketStatus.ERROR)) {
- try { socket.close(); } catch (IOException e) { }
- }
- }
- }
-
- /**
- * Timeout checks.
- */
- protected 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;
- } else {
- lastMaintain = date;
- }
- Socket socket = timeouts.check(date);
- while (socket != null) {
- if (!processSocket(socket, SocketStatus.TIMEOUT)) {
- try { socket.close(); } catch (IOException e) { }
- }
- socket = timeouts.check(date);
- }
-
- }
-
- /**
- * The background thread that listens for incoming TCP/IP connections and
- * hands them off to an appropriate processor.
- */
- public void run() {
-
- int maintain = 0;
- // Loop until we receive a shutdown command
- while (running) {
-
- // Loop if endpoint is paused
- while (paused) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- // Ignore
- }
- }
- // Check timeouts for suspended connections if the poller is empty
- while (connectionCount < 1 && addList.size() < 1) {
- // Reset maintain time.
- try {
- if (soTimeout > 0 && running) {
- maintain();
- }
- synchronized (this) {
- this.wait(10000);
- }
- } catch (InterruptedException e) {
- // Ignore
- } catch (Throwable t) {
- log.error(sm.getString("endpoint.maintain.error"), t);
- }
- }
-
- try {
-
- // Add sockets which are waiting to the poller
- if (addList.size() > 0) {
- synchronized (this) {
- // Duplicate to another list, so that the syncing is minimal
- addList.duplicate(localAddList);
- addList.clear();
- }
- SocketInfo info = localAddList.get();
- while (info != null) {
- if (info.wakeup()) {
- // Resume event if socket is present in the poller
- if (timeouts.remove(info.socket)) {
- if (info.resume()) {
- if (!processSocket(info.socket, SocketStatus.OPEN_CALLBACK)) {
- try { info.socket.close(); } catch (IOException e) { }
- }
- } else {
- timeouts.add(info.socket, System.currentTimeMillis() + info.timeout);
- }
- }
- } else {
- if (info.resume()) {
- timeouts.remove(info.socket);
- if (!processSocket(info.socket, SocketStatus.OPEN_CALLBACK)) {
- try { info.socket.close(); } catch (IOException e) { }
- }
- } else {
- timeouts.add(info.socket, System.currentTimeMillis() + info.timeout);
- }
- }
- info = localAddList.get();
- }
- }
-
- try {
- Thread.sleep(2);
- } catch (InterruptedException e) {
- // Ignore
- }
-
- // Process socket timeouts
- if (soTimeout > 0 && maintain++ > 1000 && running) {
- maintain = 0;
- maintain();
- }
-
- } catch (Throwable t) {
- if (maintain == 0) {
- log.error(sm.getString("endpoint.maintain.error"), t);
- } else {
- log.error(sm.getString("endpoint.poll.error"), t);
- }
- }
-
- }
-
- synchronized (this) {
- this.notifyAll();
- }
-
- }
-
- }
-
-
- // ----------------------------------------------------- Worker Inner Class
-
-
- protected class Worker implements Runnable {
-
- protected Thread thread = null;
- protected boolean available = false;
- protected Socket socket = null;
- protected SocketStatus status = null;
-
-
- /**
- * Process an incoming TCP/IP connection on the specified socket. Any
- * exception that occurs during processing must be logged and swallowed.
- * <b>NOTE</b>: This method is called from our Connector's thread. We
- * must assign it to our own thread so that multiple simultaneous
- * requests can be handled.
- *
- * @param socket TCP socket to process
- */
- protected synchronized void assign(Socket socket) {
-
- // Wait for the Processor to get the previous Socket
- while (available) {
- try {
- wait();
- } catch (InterruptedException e) {
- }
- }
-
- // Store the newly available Socket and notify our thread
- this.socket = socket;
- this.status = null;
- available = true;
- notifyAll();
-
- }
-
-
- protected synchronized void assign(Socket socket, SocketStatus status) {
-
- // Wait for the Processor to get the previous Socket
- while (available) {
- try {
- wait();
- } catch (InterruptedException e) {
- }
- }
-
- // Store the newly available Socket and notify our thread
- this.socket = socket;
- this.status = status;
- available = true;
- notifyAll();
-
- }
-
-
- /**
- * Await a newly assigned Socket from our Connector, or <code>null</code>
- * if we are supposed to shut down.
- */
- private synchronized Socket await() {
-
- // Wait for the Connector to provide a new Socket
- while (!available) {
- try {
- wait();
- } catch (InterruptedException e) {
- }
- }
-
- // Notify the Connector that we have received this Socket
- Socket socket = this.socket;
- available = false;
- notifyAll();
-
- return (socket);
-
- }
-
-
-
- /**
- * The background thread that listens for incoming TCP/IP connections and
- * hands them off to an appropriate processor.
- */
- public void run() {
-
- // Process requests until we receive a shutdown signal
- while (running) {
-
- // Wait for the next socket to be assigned
- Socket socket = await();
- if (socket == null)
- continue;
-
- // Process the request from this socket
- if (status != null){
- Handler.SocketState socketState = handler.event(socket, status);
- if (socketState == Handler.SocketState.CLOSED) {
- // Close socket
- try { socket.close(); } catch (IOException e) { }
- } else if (socketState == Handler.SocketState.OPEN) {
- // Process the keepalive after the event processing
- // This is the main behavior difference with endpoint with pollers, which
- // will add the socket to the poller
- if (handler.process(socket) == Handler.SocketState.CLOSED) {
- // Close socket
- try { socket.close(); } catch (IOException e) { }
- }
- }
- } else if ((status == null) && (!setSocketOptions(socket) || (handler.process(socket) == Handler.SocketState.CLOSED))) {
- // Close socket
- try { socket.close(); } catch (IOException e) { }
- }
-
- // Finish up this request
- recycleWorkerThread(this);
-
- }
-
- }
-
-
- /**
- * Start the background processing thread.
- */
- public void start() {
- thread = new Thread(this);
- thread.setName(getName() + "-" + (++curThreads));
- thread.setDaemon(true);
- thread.start();
- }
-
-
- }
-
-
- // -------------------- Public methods --------------------
-
- public void init()
- throws Exception {
-
- if (initialized)
- return;
-
- // Initialize thread count defaults for acceptor
- if (acceptorThreadCount == 0) {
- acceptorThreadCount = 1;
- }
- if (serverSocketFactory == null) {
- serverSocketFactory = ServerSocketFactory.getDefault();
- }
- if (serverSocket == null) {
- try {
- if (address == null) {
- serverSocket = serverSocketFactory.createSocket(port, backlog);
- } else {
- serverSocket = serverSocketFactory.createSocket(port, backlog, address);
- }
- } catch (BindException be) {
- if (address == null) {
- throw new BindException(be.getMessage() + "<null>:" + port);
- } else {
- throw new BindException(be.getMessage() + " " +
- address.toString() + ":" + port);
- }
- }
- }
- //if( serverTimeout >= 0 )
- // serverSocket.setSoTimeout( serverTimeout );
-
- initialized = true;
-
- }
-
- public void start()
- throws Exception {
- // Initialize socket if not done before
- if (!initialized) {
- init();
- }
- if (!running) {
- running = true;
- paused = false;
-
- // Create worker collection
- if (executor == null) {
- workers = new WorkerStack(maxThreads);
- }
-
- // Start event poller thread
- eventPoller = new Poller();
- eventPoller.init();
- Thread pollerThread = new Thread(eventPoller, getName() + "-Poller");
- pollerThread.setPriority(threadPriority);
- pollerThread.setDaemon(true);
- pollerThread.start();
-
- // Start acceptor threads
- for (int i = 0; i < acceptorThreadCount; i++) {
- Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);
- acceptorThread.setPriority(threadPriority);
- acceptorThread.setDaemon(daemon);
- acceptorThread.start();
- }
- }
- }
-
- public void pause() {
- if (running && !paused) {
- paused = true;
- unlockAccept();
- }
- }
-
- public void resume() {
- if (running) {
- paused = false;
- }
- }
-
- public void stop() {
- if (running) {
- running = false;
- unlockAccept();
- eventPoller.destroy();
- eventPoller = null;
- }
- }
-
- /**
- * Deallocate APR memory pools, and close server socket.
- */
- public void destroy() throws Exception {
- if (running) {
- stop();
- }
- if (serverSocket != null) {
- try {
- if (serverSocket != null)
- serverSocket.close();
- } catch (Exception e) {
- log.error(sm.getString("endpoint.err.close"), e);
- }
- serverSocket = null;
- }
- initialized = false ;
- }
-
-
- /**
- * Unlock the accept by using a local connection.
- */
- protected void unlockAccept() {
- Socket s = null;
- try {
- // Need to create a connection to unlock the accept();
- if (address == null) {
- s = new Socket("localhost", port);
- } else {
- s = new Socket(address, port);
- // setting soLinger to a small value will help shutdown the
- // connection quicker
- s.setSoLinger(true, 0);
- }
- } catch (Exception e) {
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("endpoint.debug.unlock", "" + port), e);
- }
- } finally {
- if (s != null) {
- try {
- s.close();
- } catch (Exception e) {
- // Ignore
- }
- }
- }
- }
-
-
- /**
- * Set the options for the current socket.
- */
- protected boolean setSocketOptions(Socket socket) {
- // Process the connection
- int step = 1;
- try {
-
- // 1: Set socket options: timeout, linger, etc
- if (soLinger >= 0) {
- socket.setSoLinger(true, soLinger);
- }
- if (tcpNoDelay) {
- socket.setTcpNoDelay(tcpNoDelay);
- }
- if (soTimeout > 0) {
- socket.setSoTimeout(soTimeout);
- }
-
- // 2: SSL handshake
- step = 2;
- serverSocketFactory.handshake(socket);
-
- } catch (Throwable t) {
- if (log.isDebugEnabled()) {
- if (step == 2) {
- log.debug(sm.getString("endpoint.err.handshake"), t);
- } else {
- log.debug(sm.getString("endpoint.err.unexpected"), t);
- }
- }
- // Tell to close the socket
- return false;
- }
- return true;
- }
-
-
- /**
- * Create (or allocate) and return an available processor for use in
- * processing a specific HTTP request, if possible. If the maximum
- * allowed processors have already been created and are in use, return
- * <code>null</code> instead.
- */
- protected Worker createWorkerThread() {
-
- synchronized (workers) {
- if (workers.size() > 0) {
- curThreadsBusy++;
- return workers.pop();
- }
- if ((maxThreads > 0) && (curThreads < maxThreads)) {
- curThreadsBusy++;
- if (curThreadsBusy == maxThreads) {
- log.info(sm.getString("endpoint.info.maxThreads",
- Integer.toString(maxThreads), address,
- Integer.toString(port)));
- }
- return (newWorkerThread());
- } else {
- if (maxThreads < 0) {
- curThreadsBusy++;
- return (newWorkerThread());
- } else {
- return (null);
- }
- }
- }
-
- }
-
-
- /**
- * Create and return a new processor suitable for processing HTTP
- * requests and returning the corresponding responses.
- */
- protected Worker newWorkerThread() {
-
- Worker workerThread = new Worker();
- workerThread.start();
- return (workerThread);
-
- }
-
-
- /**
- * Return a new worker thread, and block while to worker is available.
- */
- protected Worker getWorkerThread() {
- // Allocate a new worker thread
- Worker workerThread = createWorkerThread();
- if (org.apache.tomcat.util.net.Constants.WAIT_FOR_THREAD
- || org.apache.tomcat.util.Constants.LOW_MEMORY) {
- while (workerThread == null) {
- try {
- synchronized (workers) {
- workers.wait();
- }
- } catch (InterruptedException e) {
- // Ignore
- }
- workerThread = createWorkerThread();
- }
- }
- return workerThread;
- }
-
-
- /**
- * Recycle the specified Processor so that it can be used again.
- *
- * @param workerThread The processor to be recycled
- */
- protected void recycleWorkerThread(Worker workerThread) {
- synchronized (workers) {
- workers.push(workerThread);
- curThreadsBusy--;
- workers.notify();
- }
- }
-
-
- /**
- * Process given socket.
- */
- protected boolean processSocket(Socket socket) {
- try {
- if (executor == null) {
- Worker worker = getWorkerThread();
- if (worker != null) {
- worker.assign(socket);
- } else {
- return false;
- }
- } else {
- executor.execute(new SocketProcessor(socket));
- }
- } catch (Throwable t) {
- // This means we got an OOM or similar creating a thread, or that
- // the pool and its queue are full
- log.error(sm.getString("endpoint.process.fail"), t);
- return false;
- }
- return true;
- }
-
-
- /**
- * Process given socket for an event.
- */
- protected boolean processSocket(Socket socket, SocketStatus status) {
- try {
- if (executor == null) {
- Worker worker = getWorkerThread();
- if (worker != null) {
- worker.assign(socket, status);
- } else {
- return false;
- }
- } else {
- executor.execute(new SocketEventProcessor(socket, status));
- }
- } catch (Throwable t) {
- // This means we got an OOM or similar creating a thread, or that
- // the pool and its queue are full
- log.error(sm.getString("endpoint.process.fail"), t);
- return false;
- }
- return true;
- }
-
-
- // ------------------------------------------------- WorkerStack Inner Class
-
-
- public class WorkerStack {
-
- protected Worker[] workers = null;
- protected int end = 0;
-
- public WorkerStack(int size) {
- workers = new Worker[size];
- }
-
- /**
- * Put the object into the queue.
- *
- * @param object the object to be appended to the queue (first element).
- */
- public void push(Worker worker) {
- workers[end++] = worker;
- }
-
- /**
- * Get the first object out of the queue. Return null if the queue
- * is empty.
- */
- public Worker pop() {
- if (end > 0) {
- return workers[--end];
- }
- return null;
- }
-
- /**
- * Get the first object out of the queue, Return null if the queue
- * is empty.
- */
- public Worker peek() {
- return workers[end];
- }
-
- /**
- * Is the queue empty?
- */
- public boolean isEmpty() {
- return (end == 0);
- }
-
- /**
- * How many elements are there in this queue?
- */
- public int size() {
- return (end);
- }
- }
-
-}
Modified: trunk/java/org/apache/tomcat/util/net/SSLImplementation.java
===================================================================
--- trunk/java/org/apache/tomcat/util/net/SSLImplementation.java 2012-06-13 08:50:17 UTC (rev 2041)
+++ trunk/java/org/apache/tomcat/util/net/SSLImplementation.java 2012-06-14 14:11:34 UTC (rev 2042)
@@ -37,12 +37,8 @@
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 = { "org.apache.tomcat.util.net.jsse.NioJSSEImplementation" };
- private static final String[] implementations = { JSSEImplementationClass,
- "org.apache.tomcat.util.net.jsse.NioJSSEImplementation" };
-
/**
* @return the default implementation of {@code SSLImplementation}
* @throws ClassNotFoundException
@@ -74,13 +70,6 @@
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) {
@@ -97,24 +86,12 @@
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
*/
Deleted: trunk/java/org/apache/tomcat/util/net/ServerSocketFactory.java
===================================================================
--- trunk/java/org/apache/tomcat/util/net/ServerSocketFactory.java 2012-06-13 08:50:17 UTC (rev 2041)
+++ trunk/java/org/apache/tomcat/util/net/ServerSocketFactory.java 2012-06-14 14:11:34 UTC (rev 2042)
@@ -1,173 +0,0 @@
-/*
- * 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;
-
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.util.Hashtable;
-
-/**
- * This class creates server sockets. It may be subclassed by other
- * factories, which create particular types of server sockets. This
- * provides a general framework for the addition of public socket-level
- * functionality. It it is the server side analogue of a socket factory,
- * and similarly provides a way to capture a variety of policies related
- * to the sockets being constructed.
- *
- * <P> Like socket factories, Server Socket factory instances have two
- * categories of methods. First are methods used to create sockets.
- * Second are methods which set properties used in the production of
- * sockets, such as networking options. There is also an environment
- * specific default server socket factory; frameworks will often use
- * their own customized factory.
- *
- * <P><hr><em> It may be desirable to move this interface into the
- * <b>java.net</b> package, so that is not an extension but the preferred
- * interface. Should this be serializable, making it a JavaBean which can
- * be saved along with its networking configuration?
- * </em>
- *
- * @author db(a)eng.sun.com
- * @author Harish Prabandham
- */
-public abstract class ServerSocketFactory implements Cloneable {
-
- //
- // NOTE: JDK 1.1 bug in class GC, this can get collected
- // even though it's always accessible via getDefault().
- //
-
- private static ServerSocketFactory theFactory;
- protected Hashtable attributes=new Hashtable();
-
- /**
- * Constructor is used only by subclasses.
- */
-
- protected ServerSocketFactory () {
- /* NOTHING */
- }
-
- /** General mechanism to pass attributes from the
- * ServerConnector to the socket factory.
- *
- * Note that the "prefered" mechanism is to
- * use bean setters and explicit methods, but
- * this allows easy configuration via server.xml
- * or simple Properties
- */
- 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.
- */
- public static synchronized ServerSocketFactory getDefault () {
- //
- // 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 DefaultServerSocketFactory ();
- }
-
- try {
- return (ServerSocketFactory) theFactory.clone ();
- } catch (CloneNotSupportedException e) {
- throw new RuntimeException (e.getMessage ());
- }
- }
-
- /**
- * Returns a server socket 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
- * @exception IOException for networking errors
- * @exception InstantiationException for construction errors
- */
- public abstract ServerSocket createSocket (int port)
- throws IOException, InstantiationException;
-
- /**
- * 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
- * @exception IOException for networking errors
- * @exception InstantiationException for construction errors
- */
-
- public abstract ServerSocket createSocket (int port, int backlog)
- throws IOException, InstantiationException;
-
- /**
- * Returns a server socket 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
- * @exception IOException for networking errors
- * @exception InstantiationException for construction errors
- */
-
- public abstract ServerSocket createSocket (int port,
- int backlog, InetAddress ifAddress)
- throws IOException, InstantiationException;
-
- public void initSocket( Socket s ) {
- }
-
- /**
- Wrapper function for accept(). This allows us to trap and
- translate exceptions if necessary
-
- @exception IOException;
- */
- public abstract Socket acceptSocket(ServerSocket socket)
- throws IOException;
-
- /**
- Extra function to initiate the handshake. Sometimes necessary
- for SSL
-
- @exception IOException;
- */
- public abstract void handshake(Socket sock)
- throws IOException;
-}
-
Deleted: trunk/java/org/apache/tomcat/util/net/jsse/JSSEFactory.java
===================================================================
--- trunk/java/org/apache/tomcat/util/net/jsse/JSSEFactory.java 2012-06-13 08:50:17 UTC (rev 2041)
+++ trunk/java/org/apache/tomcat/util/net/jsse/JSSEFactory.java 2012-06-14 14:11:34 UTC (rev 2042)
@@ -1,56 +0,0 @@
-/*
- * 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 javax.net.ssl.SSLSocket;
-
-import org.apache.tomcat.util.net.SSLSupport;
-import org.apache.tomcat.util.net.ServerSocketFactory;
-import javax.net.ssl.SSLSession;
-
-/**
- * Factory interface to construct components based on the JSSE version
- * in use.
- *
- * @author Bill Barker
- * @author Filip Hanik
- */
-
-public class JSSEFactory {
-
- /**
- * Returns the ServerSocketFactory to use.
- */
- public ServerSocketFactory getSocketFactory() {
- return new JSSESocketFactory();
- }
-
- /**
- * returns the SSLSupport attached to this socket.
- */
- public SSLSupport getSSLSupport(Socket socket) {
- return new JSSESupport((SSLSocket)socket);
- }
-
- public SSLSupport getSSLSupport(SSLSession session) {
- return new JSSESupport(session);
- }
-
-};
Deleted: trunk/java/org/apache/tomcat/util/net/jsse/JSSEImplementation.java
===================================================================
--- trunk/java/org/apache/tomcat/util/net/jsse/JSSEImplementation.java 2012-06-13 08:50:17 UTC (rev 2041)
+++ trunk/java/org/apache/tomcat/util/net/jsse/JSSEImplementation.java 2012-06-14 14:11:34 UTC (rev 2042)
@@ -1,126 +0,0 @@
-/*
- * 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;
-
-/**
- * {@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);
-
- 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";
- }
-
- /*
- * (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
Deleted: trunk/java/org/apache/tomcat/util/net/jsse/JSSESocketFactory.java
===================================================================
--- trunk/java/org/apache/tomcat/util/net/jsse/JSSESocketFactory.java 2012-06-13 08:50:17 UTC (rev 2041)
+++ trunk/java/org/apache/tomcat/util/net/jsse/JSSESocketFactory.java 2012-06-14 14:11:34 UTC (rev 2042)
@@ -1,812 +0,0 @@
-/*
- * 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.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.InetAddress;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.SocketException;
-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.HandshakeCompletedEvent;
-import javax.net.ssl.HandshakeCompletedListener;
-import javax.net.ssl.KeyManager;
-import javax.net.ssl.KeyManagerFactory;
-import javax.net.ssl.ManagerFactoryParameters;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLException;
-import javax.net.ssl.SSLServerSocket;
-import javax.net.ssl.SSLServerSocketFactory;
-import javax.net.ssl.SSLSession;
-import javax.net.ssl.SSLSessionContext;
-import javax.net.ssl.SSLSocket;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.TrustManagerFactory;
-import javax.net.ssl.X509KeyManager;
-
-import org.apache.tomcat.util.res.StringManager;
-
-/*
- 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 )
- */
-
-/**
- * SSL server socket factory. It _requires_ a valid RSA key and
- * JSSE.
- *
- * @author Harish Prabandham
- * @author Costin Manolache
- * @author Stefan Freyr Stefansson
- * @author EKR -- renamed to JSSESocketFactory
- * @author Jan Luehe
- * @author Bill Barker
- */
-public class JSSESocketFactory
- extends org.apache.tomcat.util.net.ServerSocketFactory {
-
- private static StringManager sm =
- StringManager.getManager("org.apache.tomcat.util.net.jsse.res");
- private static final boolean RFC_5746_SUPPORTED;
- // defaults
- static 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;
-
- static org.jboss.logging.Logger log =
- org.jboss.logging.Logger.getLogger(JSSESocketFactory.class);
-
- static {
- boolean result = false;
- SSLContext context;
- try {
- context = SSLContext.getInstance("TLS");
- 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 e) {
- // Assume no RFC 5746 support
- } catch (KeyManagementException e) {
- // Assume no RFC 5746 support
- }
- RFC_5746_SUPPORTED = result;
- }
-
- protected boolean initialized;
- protected String clientAuth = "false";
- protected SSLServerSocketFactory sslProxy = null;
- 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;
-
-
- public JSSESocketFactory () {
- }
-
- public ServerSocket createSocket (int port)
- throws IOException
- {
- if (!initialized) init();
- ServerSocket socket = sslProxy.createServerSocket(port);
- initServerSocket(socket);
- return socket;
- }
-
- public ServerSocket createSocket (int port, int backlog)
- throws IOException
- {
- if (!initialized) init();
- ServerSocket socket = sslProxy.createServerSocket(port, backlog);
- initServerSocket(socket);
- return socket;
- }
-
- public ServerSocket createSocket (int port, int backlog,
- InetAddress ifAddress)
- throws IOException
- {
- if (!initialized) init();
- ServerSocket socket = sslProxy.createServerSocket(port, backlog,
- ifAddress);
- initServerSocket(socket);
- return socket;
- }
-
- public Socket acceptSocket(ServerSocket socket)
- throws IOException
- {
- SSLSocket asock = null;
- try {
- asock = (SSLSocket)socket.accept();
- } catch (SSLException e){
- throw new SocketException("SSL handshake error" + e.toString());
- }
- return asock;
- }
-
- public void handshake(Socket sock) throws IOException {
- // We do getSession instead of startHandshake() so we can call this multiple times
- SSLSession session = ((SSLSocket)sock).getSession();
- if (session.getCipherSuite().equals("SSL_NULL_WITH_NULL_NULL"))
- throw new IOException("SSL handshake failed. Ciper suite in SSL Session is SSL_NULL_WITH_NULL_NULL");
-
- if (!allowUnsafeLegacyRenegotiation && !RFC_5746_SUPPORTED) {
- // Prevent further handshakes by removing all cipher suites
- ((SSLSocket) sock).setEnabledCipherSuites(new String[0]);
- }
- }
-
- /*
- * 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;
-
- if (requestedCiphers != null) {
- Vector 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 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 SSL server's keystore.
- */
- 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 truststore.
- */
- 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;
- }
-
- /*
- * Gets the key- or truststore with the specified type, path, and password.
- */
- 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;
- }
-
- /**
- * Reads the keystore and initializes the SSL socket factory.
- */
- 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 init SSLContext
- SSLContext context = (SSLContext) attributes.get("SSLContext");
- if (context == null) {
- context = SSLContext.getInstance(protocol);
- context.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 =
- context.getServerSessionContext();
- if (sessionContext != null) {
- sessionContext.setSessionCacheSize(sessionCacheSize);
- sessionContext.setSessionTimeout(sessionCacheTimeout);
- }
-
- // create proxy
- sslProxy = context.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 the SSL config is OK
- checkConfig();
-
- } catch(Exception e) {
- if( e instanceof IOException )
- throw (IOException)e;
- throw new IOException(e.getMessage());
- }
- }
-
- /**
- * Gets the initialized key managers.
- */
- 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 (JSSESocketFactory.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 intialized trust managers.
- */
- 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;
- }
-
- /**
- * 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.
- *
- */
- 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 socket the SSLServerSocket.
- * @param protocols the protocols to use.
- */
- protected void setEnabledProtocols(SSLServerSocket socket, String []protocols){
- if (protocols != null) {
- socket.setEnabledProtocols(protocols);
- }
- }
-
- /**
- * Determines the SSL protocol variants to be enabled.
- *
- * @param socket The socket 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(SSLServerSocket socket,
- String requestedProtocols){
- String[] supportedProtocols = socket.getSupportedProtocols();
-
- String[] enabledProtocols = null;
-
- if (requestedProtocols != null) {
- Vector vec = null;
- String protocol = requestedProtocols;
- int index = requestedProtocols.indexOf(',');
- if (index != -1) {
- int fromIndex = 0;
- while (index != -1) {
- protocol = requestedProtocols.substring(fromIndex, index).trim();
- if (protocol.length() > 0) {
- /*
- * Check to see if the requested protocol is among the
- * supported protocols, i.e., may be enabled
- */
- for (int i=0; supportedProtocols != null
- && i<supportedProtocols.length; i++) {
- if (supportedProtocols[i].equals(protocol)) {
- if (vec == null) {
- vec = new Vector();
- }
- vec.addElement(protocol);
- break;
- }
- }
- }
- fromIndex = index+1;
- index = requestedProtocols.indexOf(',', fromIndex);
- } // while
- protocol = requestedProtocols.substring(fromIndex);
- }
-
- if (protocol != null) {
- protocol = protocol.trim();
- if (protocol.length() > 0) {
- /*
- * Check to see if the requested protocol is among the
- * supported protocols, i.e., may be enabled
- */
- for (int i=0; supportedProtocols != null
- && i<supportedProtocols.length; i++) {
- if (supportedProtocols[i].equals(protocol)) {
- if (vec == null) {
- vec = new Vector();
- }
- vec.addElement(protocol);
- break;
- }
- }
- }
- }
-
- if (vec != null) {
- enabledProtocols = new String[vec.size()];
- vec.copyInto(enabledProtocols);
- }
- }
-
- return enabledProtocols;
- }
-
- /**
- * Configure Client authentication for this version of JSSE. The
- * JSSE included in Java 1.4 supports the 'want' value. Prior
- * versions of JSSE will treat 'want' as 'false'.
- * @param socket the SSLServerSocket
- */
- protected void configureClientAuth(SSLServerSocket socket){
- if (wantClientAuth){
- socket.setWantClientAuth(wantClientAuth);
- } else {
- socket.setNeedClientAuth(requireClientAuth);
- }
- }
-
- /**
- * Configure Client authentication for this version of JSSE. The
- * JSSE included in Java 1.4 supports the 'want' value. Prior
- * versions of JSSE will treat 'want' as 'false'.
- * @param socket the SSLSocket
- */
- protected void configureClientAuth(SSLSocket socket){
- // Per JavaDocs: SSLSockets returned from
- // SSLServerSocket.accept() inherit this setting.
- }
-
- /**
- * Configures the given SSL server socket with the requested cipher suites,
- * protocol versions, and need for client authentication
- */
- private void initServerSocket(ServerSocket ssocket) {
-
- SSLServerSocket socket = (SSLServerSocket) ssocket;
-
- if (enabledCiphers != null) {
- socket.setEnabledCipherSuites(enabledCiphers);
- }
-
- String requestedProtocols = (String) attributes.get("protocols");
- setEnabledProtocols(socket, getEnabledProtocols(socket,
- requestedProtocols));
-
- // we don't know if client auth is needed -
- // after parsing the request we may re-handshake
- configureClientAuth(socket);
- }
-
- /**
- * 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
- ServerSocket socket = sslProxy.createServerSocket();
- initServerSocket(socket);
-
- 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 behaviour 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();
- }
- }
-
- }
-
-}
Deleted: trunk/java/org/apache/tomcat/util/net/jsse/JSSESupport.java
===================================================================
--- trunk/java/org/apache/tomcat/util/net/jsse/JSSESupport.java 2012-06-13 08:50:17 UTC (rev 2041)
+++ trunk/java/org/apache/tomcat/util/net/jsse/JSSESupport.java 2012-06-14 14:11:34 UTC (rev 2042)
@@ -1,246 +0,0 @@
-/*
- * 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.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.SocketException;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateFactory;
-
-import javax.net.ssl.HandshakeCompletedEvent;
-import javax.net.ssl.HandshakeCompletedListener;
-import javax.net.ssl.SSLException;
-import javax.net.ssl.SSLSession;
-import javax.net.ssl.SSLSocket;
-import javax.security.cert.X509Certificate;
-
-import org.apache.tomcat.util.net.Constants;
-import org.apache.tomcat.util.net.SSLSupport;
-
-/** JSSESupport
-
- Concrete implementation class for JSSE
- Support classes.
-
- This will only work with JDK 1.2 and up since it
- depends on JDK 1.2's certificate support
-
- @author EKR
- @author Craig R. McClanahan
- @author Filip Hanik
- Parts cribbed from JSSECertCompat
- Parts cribbed from CertificatesValve
-*/
-
-class JSSESupport implements SSLSupport {
-
- private static org.jboss.logging.Logger log =
- org.jboss.logging.Logger.getLogger(JSSESupport.class);
-
- protected SSLSocket ssl;
- protected SSLSession session;
-
- Listener listener = new Listener();
-
- JSSESupport(SSLSocket sock){
- ssl=sock;
- session = sock.getSession();
- sock.addHandshakeCompletedListener(listener);
- }
-
- JSSESupport(SSLSession session) {
- this.session = session;
- }
-
- public String getCipherSuite() throws IOException {
- // Look up the current SSLSession
- if (session == null)
- return null;
- return session.getCipherSuite();
- }
-
- public Object[] getPeerCertificateChain()
- throws IOException {
- return getPeerCertificateChain(false);
- }
-
- 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;
- }
-
- 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 = ssl.getSession();
- }
- return getX509Certificates(session);
- }
-
- protected void handShake() throws IOException {
- if( ssl.getWantClientAuth() ) {
- log.debug("No client cert sent for want");
- } else {
- ssl.setNeedClientAuth(true);
- }
-
- if (ssl.getEnabledCipherSuites().length == 0) {
- // Handshake is never going to be successful.
- // Assume this is because handshakes are disabled
- log.warn("SSL server initiated renegotiation is disabled, closing connection");
- session.invalidate();
- ssl.close();
- return;
- }
-
- InputStream in = ssl.getInputStream();
- int oldTimeout = ssl.getSoTimeout();
- ssl.setSoTimeout(1000);
- byte[] b = new byte[0];
- listener.reset();
- ssl.startHandshake();
- int maxTries = 60; // 60 * 1000 = example 1 minute time out
- for (int i = 0; i < maxTries; i++) {
- if(log.isTraceEnabled())
- log.trace("Reading for try #" +i);
- try {
- int x = in.read(b);
- } catch(SSLException sslex) {
- log.info("SSL Error getting client Certs",sslex);
- throw sslex;
- } catch (IOException e) {
- // ignore - presumably the timeout
- }
- if (listener.completed) {
- break;
- }
- }
- ssl.setSoTimeout(oldTimeout);
- if (listener.completed == false) {
- throw new SocketException("SSL Cert handshake timeout");
- }
-
- }
-
- /**
- * Copied from <code>org.apache.catalina.valves.CertificateValve</code>
- */
- 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 = Integer.valueOf(size);
- session.putValue(Constants.KEY_SIZE_KEY, keySize);
- }
- return keySize;
- }
-
- 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();
- }
-
-
- private static class Listener implements HandshakeCompletedListener {
- volatile boolean completed = false;
- public void handshakeCompleted(HandshakeCompletedEvent event) {
- completed = true;
- }
- void reset() {
- completed = false;
- }
- }
-
-}
-
Modified: trunk/java/org/apache/tomcat/util/net/jsse/NioJSSEImplementation.java
===================================================================
--- trunk/java/org/apache/tomcat/util/net/jsse/NioJSSEImplementation.java 2012-06-13 08:50:17 UTC (rev 2041)
+++ trunk/java/org/apache/tomcat/util/net/jsse/NioJSSEImplementation.java 2012-06-14 14:11:34 UTC (rev 2042)
@@ -28,7 +28,6 @@
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}
@@ -100,27 +99,4 @@
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());
- }
-
}
Modified: trunk/webapps/docs/changelog.xml
===================================================================
--- trunk/webapps/docs/changelog.xml 2012-06-13 08:50:17 UTC (rev 2041)
+++ trunk/webapps/docs/changelog.xml 2012-06-14 14:11:34 UTC (rev 2042)
@@ -16,7 +16,7 @@
<body>
-<section name="JBoss Web 7.2.0.Alpha1 (remm)">
+<section name="JBoss Web 7.7.0.Alpha1 (remm)">
<subsection name="Catalina">
<changelog>
<fix>
@@ -62,6 +62,9 @@
<add>
NIO 2 HTTP connector. (benothman)
</add>
+ <add>
+ Drop java.io connectors (AJP and HTTP). AJP now requires native. (remm)
+ </add>
</changelog>
</subsection>
<subsection name="Jasper">
12 years, 6 months
JBossWeb SVN: r2041 - in trunk: java/org/apache/coyote/http11 and 3 other directories.
by jbossweb-commits@lists.jboss.org
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">
12 years, 6 months
JBossWeb SVN: r2040 - in trunk: java/org/apache/catalina/core and 1 other directories.
by jbossweb-commits@lists.jboss.org
Author: remy.maucherat(a)jboss.com
Date: 2012-06-07 09:48:42 -0400 (Thu, 07 Jun 2012)
New Revision: 2040
Modified:
trunk/java/org/apache/catalina/connector/CoyoteAdapter.java
trunk/java/org/apache/catalina/connector/Request.java
trunk/java/org/apache/catalina/core/StandardWrapperValve.java
trunk/webapps/docs/changelog.xml
Log:
AS7-4232: Modify async error processing based on a stricter spec interpretation (not really tested yet)
Modified: trunk/java/org/apache/catalina/connector/CoyoteAdapter.java
===================================================================
--- trunk/java/org/apache/catalina/connector/CoyoteAdapter.java 2012-06-05 21:37:52 UTC (rev 2039)
+++ trunk/java/org/apache/catalina/connector/CoyoteAdapter.java 2012-06-07 13:48:42 UTC (rev 2040)
@@ -264,7 +264,9 @@
// Calling the container
connector.getContainer().getPipeline().getFirst().event(request, response, request.getEvent());
- if (!error && (request.getAttribute(RequestDispatcher.ERROR_EXCEPTION) != null)) {
+ Request.AsyncContextImpl asyncContext = (Request.AsyncContextImpl) request.getAsyncContext();
+ if (!error && ((request.getAttribute(RequestDispatcher.ERROR_EXCEPTION) != null)
+ || (asyncContext != null && asyncContext.getError() != null))) {
// An unexpected exception occurred while processing the event, so
// error should be called
request.getEvent().setType(HttpEvent.EventType.ERROR);
@@ -344,6 +346,9 @@
req.getParameters().setQueryStringEncoding
(connector.getURIEncoding());
+ // Set the context classloader, since in that case it might not be set
+ Thread.currentThread().setContextClassLoader(CoyoteAdapter.class.getClassLoader());
+
}
// Check if the connector was stopped
Modified: trunk/java/org/apache/catalina/connector/Request.java
===================================================================
--- trunk/java/org/apache/catalina/connector/Request.java 2012-06-05 21:37:52 UTC (rev 2039)
+++ trunk/java/org/apache/catalina/connector/Request.java 2012-06-07 13:48:42 UTC (rev 2040)
@@ -3368,6 +3368,7 @@
protected ServletContext servletContext = null;
protected String path = null;
protected Runnable runnable = null;
+ protected Throwable error = null;
protected boolean useAttributes = false;
protected boolean original = true;
protected boolean ready = true;
@@ -3469,6 +3470,7 @@
runnable = null;
useAttributes = false;
ready = true;
+ error = null;
}
public Map<AsyncListener, AsyncListenerRegistration> getAsyncListeners() {
@@ -3509,6 +3511,15 @@
return listenerInstance;
}
+ public Throwable getError() {
+ return error;
+ }
+
+ public void setError(Throwable error) {
+ ready = true;
+ this.error = error;
+ }
+
}
Modified: trunk/java/org/apache/catalina/core/StandardWrapperValve.java
===================================================================
--- trunk/java/org/apache/catalina/core/StandardWrapperValve.java 2012-06-05 21:37:52 UTC (rev 2039)
+++ trunk/java/org/apache/catalina/core/StandardWrapperValve.java 2012-06-07 13:48:42 UTC (rev 2040)
@@ -583,9 +583,8 @@
asyncListenerRegistration.getRequest(), asyncListenerRegistration.getResponse());
asyncListener.onTimeout(asyncEvent);
} else if (error) {
- Throwable t = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
AsyncEvent asyncEvent = new AsyncEvent(asyncContext,
- asyncListenerRegistration.getRequest(), asyncListenerRegistration.getResponse(), t);
+ asyncListenerRegistration.getRequest(), asyncListenerRegistration.getResponse(), asyncContext.getError());
asyncListener.onError(asyncEvent);
} else {
AsyncEvent asyncEvent = new AsyncEvent(asyncContext,
@@ -601,6 +600,9 @@
if (timeout && request.isEventMode() && asyncContext.getPath() == null) {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
+ if (error && request.isEventMode() && asyncContext.getPath() == null) {
+ exception(request, response, asyncContext.getError());
+ }
} else if (asyncContext.getRunnable() != null) {
// Execute the runnable
try {
@@ -608,7 +610,7 @@
} catch (Throwable e) {
container.getLogger().error(sm.getString("standardWrapper.async.runnableError",
getContainer().getName()), e);
- exception(request, response, e);
+ asyncContext.setError(e);
}
} else if (asyncContext.getPath() != null) {
// We executed the dispatch
@@ -631,7 +633,7 @@
} catch (Throwable e) {
container.getLogger().error(sm.getString("standardWrapper.async.dispatchError",
getContainer().getName()), e);
- exception(request, response, e);
+ asyncContext.setError(e);
}
request.setCanStartAsync(false);
// If there is no new startAsync, then close the response
Modified: trunk/webapps/docs/changelog.xml
===================================================================
--- trunk/webapps/docs/changelog.xml 2012-06-05 21:37:52 UTC (rev 2039)
+++ trunk/webapps/docs/changelog.xml 2012-06-07 13:48:42 UTC (rev 2040)
@@ -28,6 +28,14 @@
<fix>
Use the thread listener bind/unbind in the background process. (remm)
</fix>
+ <fix>
+ <jboss-jira>JBPAPP-9126</jboss-jira>: Set the context classloader as soon as possible on newly
+ created threads. (remm)
+ </fix>
+ <fix>
+ <jboss-jira>AS7-4232</jboss-jira>: Modify async error processing based on a stricter spec
+ interpretation. (remm)
+ </fix>
</changelog>
</subsection>
<subsection name="Coyote">
12 years, 6 months
JBossWeb SVN: r2039 - branches/JBOSSWEB_2_1_10_GA_JBPAPP_9261/java/org/apache/tomcat/util/net/jsse.
by jbossweb-commits@lists.jboss.org
Author: dehort
Date: 2012-06-05 17:37:52 -0400 (Tue, 05 Jun 2012)
New Revision: 2039
Modified:
branches/JBOSSWEB_2_1_10_GA_JBPAPP_9261/java/org/apache/tomcat/util/net/jsse/JSSESupport.java
Log:
Applying potential fix for a memory leak when using SSL and the java security manager
[JBPAPP-9261]
Modified: branches/JBOSSWEB_2_1_10_GA_JBPAPP_9261/java/org/apache/tomcat/util/net/jsse/JSSESupport.java
===================================================================
--- branches/JBOSSWEB_2_1_10_GA_JBPAPP_9261/java/org/apache/tomcat/util/net/jsse/JSSESupport.java 2012-06-05 20:53:01 UTC (rev 2038)
+++ branches/JBOSSWEB_2_1_10_GA_JBPAPP_9261/java/org/apache/tomcat/util/net/jsse/JSSESupport.java 2012-06-05 21:37:52 UTC (rev 2039)
@@ -23,6 +23,8 @@
import java.net.SocketException;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
+import java.util.Map;
+import java.util.WeakHashMap;
import javax.net.ssl.HandshakeCompletedEvent;
import javax.net.ssl.HandshakeCompletedListener;
@@ -52,6 +54,8 @@
private static org.jboss.logging.Logger log =
org.jboss.logging.Logger.getLogger(JSSESupport.class);
+ private static final Map<SSLSession, Integer> keySizeCache =
+ new WeakHashMap<SSLSession, Integer>(10);
protected SSLSocket ssl;
protected SSLSession session;
@@ -189,7 +193,7 @@
SSLSupport.CipherData c_aux[]=ciphers;
if (session == null)
return null;
- Integer keySize = (Integer) session.getValue(KEY_SIZE_KEY);
+ Integer keySize = (Integer) keySizeCache.get(session);
if (keySize == null) {
int size = 0;
String cipherSuite = session.getCipherSuite();
@@ -200,7 +204,7 @@
}
}
keySize = new Integer(size);
- session.putValue(KEY_SIZE_KEY, keySize);
+ keySizeCache.put(session, keySize);
}
return keySize;
}
12 years, 6 months
JBossWeb SVN: r2038 - branches.
by jbossweb-commits@lists.jboss.org
Author: dehort
Date: 2012-06-05 16:53:01 -0400 (Tue, 05 Jun 2012)
New Revision: 2038
Added:
branches/JBOSSWEB_2_1_10_GA_JBPAPP_9261/
Log:
Creating a branch for a one off patch
[JBPAPP-9261]
12 years, 6 months