Author: jfrederic.clere(a)jboss.com
Date: 2007-11-01 12:27:49 -0400 (Thu, 01 Nov 2007)
New Revision: 333
Added:
sandbox/tomcat/tomcat6/nio.patch
Log:
Add the remove of nio.
Added: sandbox/tomcat/tomcat6/nio.patch
===================================================================
--- sandbox/tomcat/tomcat6/nio.patch (rev 0)
+++ sandbox/tomcat/tomcat6/nio.patch 2007-11-01 16:27:49 UTC (rev 333)
@@ -0,0 +1,12434 @@
+Index: webapps/docs/config/http.xml
+===================================================================
+--- webapps/docs/config/http.xml (revision 590752)
++++ webapps/docs/config/http.xml (working copy)
+@@ -386,191 +386,10 @@
+
+ </subsection>
+
+- <subsection name="Nio Implementation">
++<subsection name="Nio Implementation">
+
+- <p>The NIO connector exposes all the low level socket properties that can be
used to tune the connector.
+- Most of these attributes are directly linked to the socket implementation in the JDK
so you can find out
+- about the actual meaning in the JDK API documentation.<br/>
+- <strong>Note</strong>On some JDK versions, setTrafficClass causes a
problem, a work around for this is to add
+- the <code>-Djava.net.preferIPv4Stack=true</code> value to your command
line</p>
++ <p>The NIO connector is not supported in this distribution, you may use <a
href="apr.html">APR</a> instead</p>
+
+- <attributes>
+- <attribute name="useSendfile" required="false">
+- <p>(bool)Use this attribute to enable or disable sendfile capability.
+- The default value is <code>true</code>
+- </p>
+- </attribute>
+- <attribute name="useExecutor" required="false">
+- <p>(bool)Set to true to use the NIO thread pool executor. The default
value is <code>true</code>.
+- If set to false, it uses a thread pool based on a stack for its execution.
+- Generally, using the executor yields a little bit slower performance, but yields
a better
+- fairness for processing connections in a high load environment as the traffic
gets queued through a
+- FIFO queue. If set to true(default) then the max pool size is the
<code>maxThreads</code> attribute
+- and the core pool size is the <code>minSpareThreads</code>.
+- This value is ignored if the <code>executor</code> attribute is
present and points to a valid shared thread pool.
+- </p>
+- </attribute>
+- <attribute name="acceptorThreadCount" required="false">
+- <p>(int)The number of threads to be used to accept connections. Increase
this value on a multi CPU machine,
+- although you would never really need more than <code>2</code>. Also,
with a lot of non keep alive connections,
+- you might want to increase this value as well. Default value is
<code>1</code>.</p>
+- </attribute>
+- <attribute name="pollerThreadCount" required="false">
+- <p>(int)The number of threads to be used to run for the polling events.
Default value is <code>1</code>.
+- Can't see a reason to go above that. But experiment and find your own
results.</p>
+- </attribute>
+- <attribute name="pollerThreadPriority"
required="false">
+- <p>(int)The priority of the poller threads.
+- The default value is <code>java.lang.Thread#NORM_PRIORITY</code>.
+- See the JavaDoc for the java.lang.Thread class for more details on
+- what this priority means.
+- </p>
+- </attribute>
+- <attribute name="acceptorThreadPriority"
required="false">
+- <p>(int)The priority of the acceptor threads. The threads used to accept
new connections.
+- The default value is <code>java.lang.Thread#NORM_PRIORITY</code>.
+- See the JavaDoc for the java.lang.Thread class for more details on
+- what this priority means.
+- </p>
+- </attribute>
+-
+- <attribute name="selectorTimeout" required="false">
+- <p>(int)The time in milliseconds to timeout on a select() for the poller.
+- This value is important, since connection clean up is done on the same
thread, so dont set this
+- value to an extremely high one. The default value is
<code>1000</code> milliseconds.</p>
+- </attribute>
+- <attribute name="useComet" required="false">
+- <p>(bool)Whether to allow comet servlets or not, Default value is
<code>true</code>.</p>
+- </attribute>
+- <attribute name="processCache" required="false">
+- <p>(int)The protocol handler caches Http11NioProcessor objects to speed up
performance.
+- This setting dictates how many of these objects get cached.
+- <code>-1</code> means unlimited, default is
<code>200</code>. Set this value somewhere close to your maxThreads value.
+- </p>
+- </attribute>
+- <attribute name="socket.directBuffer" required="false">
+- <p>(bool)Boolean value, whether to use direct ByteBuffers or java mapped
ByteBuffers. Default is <code>false</code>
+- <br/>When you are using direct buffers, make sure you allocate the
appropriate amount of memory for the
+- direct memory space. On Sun's JDK that would be something like
<code>-XX:MaxDirectMemorySize=256m</code></p>
+- </attribute>
+- <attribute name="socket.rxBufSize" required="false">
+- <p>(int)The socket receive buffer (SO_RCVBUF) size in bytes. Default value
is <code>25188</code></p>
+- </attribute>
+- <attribute name="socket.txBufSize" required="false">
+- <p>(int)The socket send buffer (SO_SNDBUF) size in bytes. Default value is
<code>43800</code></p>
+- </attribute>
+- <attribute name="socket.appReadBufSize"
required="false">
+- <p>(int)Each connection that is opened up in Tomcat get associated with a
read and a write ByteBuffer
+- This attribute controls the size of these buffers. By default this read
buffer is sized at <code>8192</code> bytes.
+- For lower concurrency, you can increase this to buffer more data.
+- For an extreme amount of keep alive connections, decrease this number or
increase your heap size.</p>
+- </attribute>
+- <attribute name="socket.appWriteBufSize"
required="false">
+- <p>(int)Each connection that is opened up in Tomcat get associated with a
read and a write ByteBuffer
+- This attribute controls the size of these buffers. By default this write
buffer is sized at <code>8192</code> bytes.
+- For low concurrency you can increase this to buffer more response data.
+- For an extreme amount of keep alive connections, decrease this number or
increase your heap size.
+- <br/>
+- The default value here is pretty low, you should up it if you are not dealing
with tens of thousands
+- concurrent connections.</p>
+- </attribute>
+- <attribute name="socket.bufferPool" required="false">
+- <p>(int)The Nio connector uses a class called NioChannel that holds
elements linked to a socket.
+- To reduce garbage collection, the Nio connector caches these channel
objects.
+- This value specifies the size of this cache.
+- The default value is <code>500</code>, and represents that the
cache will hold 500 NioChannel objects.
+- Other values are <code>-1</code>. unlimited cache, and
<code>0</code>, no cache.</p>
+- </attribute>
+- <attribute name="socket.bufferPoolSize"
required="false">
+- <p>(int)The NioChannel pool can also be size based, not used object based.
The size is calculated as follows:<br/>
+- NioChannel <code>buffer size = read buffer size + write buffer
size</code><br/>
+- SecureNioChannel <code>buffer size = application read buffer size +
application write buffer size + network read buffer size + network write buffer
size</code><br/>
+- The value is in bytes, the default value is
<code>1024*1024*100</code> (100MB)
+- </p>
+- </attribute>
+- <attribute name="socket.processorCache"
required="false">
+- <p>(int)Tomcat will cache SocketProcessor objects to reduce garbage
collection.
+- The integer value specifies how many objects to keep in the cache at most.
+- The default is <code>500</code>.
+- Other values are <code>-1</code>. unlimited cache, and
<code>0</code>, no cache.</p>
+- </attribute>
+- <attribute name="socket.keyCache" required="false">
+- <p>(int)Tomcat will cache KeyAttachment objects to reduce garbage
collection.
+- The integer value specifies how many objects to keep in the cache at most.
+- The default is <code>500</code>.
+- Other values are <code>-1</code>. unlimited cache, and
<code>0</code>, no cache.</p>
+- </attribute>
+- <attribute name="socket.eventCache" required="false">
+- <p>(int)Tomcat will cache PollerEvent objects to reduce garbage
collection.
+- The integer value specifies how many objects to keep in the cache at most.
+- The default is <code>500</code>.
+- Other values are <code>-1</code>. unlimited cache, and
<code>0</code>, no cache.</p>
+- </attribute>
+- <attribute name="socket.tcpNoDelay" required="false">
+- <p>(bool)same as the standard setting <code>tcpNoDelay</code>.
Default value is <code>false</code></p>
+- </attribute>
+- <attribute name="socket.soKeepAlive" required="false">
+- <p>(bool)Boolean value for the socket's keep alive setting
(SO_KEEPALIVE). Default is <code>false</code>. </p>
+- </attribute>
+- <attribute name="socket.ooBInline" required="false">
+- <p>(bool)Boolean value for the socket OOBINLINE setting. Default value is
<code>true</code></p>
+- </attribute>
+- <attribute name="socket.soReuseAddress"
required="false">
+- <p>(bool)Boolean value for the sockets reuse address option
(SO_REUSEADDR). Default value is <code>true</code></p>
+- </attribute>
+- <attribute name="socket.soLingerOn" required="false">
+- <p>(bool)Boolean value for the sockets so linger option (SO_LINGER).
Default value is <code>true</code>.
+- This option is paired with the <code>soLingerTime</code>
value.</p>
+- </attribute>
+- <attribute name="socket.soLingerTime" required="false">
+- <p>(bool)Value in seconds for the sockets so linger option (SO_LINGER).
Default value is <code>25</code> seconds.
+- This option is paired with the soLinger value.</p>
+- </attribute>
+- <attribute name="socket.soTimeout" required="false">
+- <p>(int)Value in milliseconds for the sockets read timeout (SO_TIMEOUT).
Default value is <code>5000</code> milliseconds.</p>
+- </attribute>
+- <attribute name="socket.soTrafficClass"
required="false">
+- <p>(byte)Value between <code>0</code> and
<code>255</code> for the traffic class on the socket, <code>0x04 | 0x08
| 0x010</code></p>
+- </attribute>
+- <attribute name="socket.performanceConnectionTime"
required="false">
+- <p>(int)The first value for the performance settings. Default is
<code>1</code>, see <a
href="http://java.sun.com/j2se/1.5.0/docs/api/java/net/Socket.html#s...
Performance Options</a></p>
+- </attribute>
+- <attribute name="socket.performanceLatency"
required="false">
+- <p>(int)The second value for the performance settings. Default is
<code>0</code>, see <a
href="http://java.sun.com/j2se/1.5.0/docs/api/java/net/Socket.html#s...
Performance Options</a></p>
+- </attribute>
+- <attribute name="socket.performanceBandwidth"
required="false">
+- <p>(int)The third value for the performance settings. Default is
<code>1</code>, see <a
href="http://java.sun.com/j2se/1.5.0/docs/api/java/net/Socket.html#s...
Performance Options</a></p>
+- </attribute>
+- <attribute name="selectorPool.maxSelectors"
required="false">
+- <p>(int)The max selectors to be used in the pool, to reduce selector
contention.
+- Use this option when the command line
<code>org.apache.tomcat.util.net.NioSelectorShared</code> value is set to
false.
+- Default value is <code>200</code>.</p>
+- </attribute>
+- <attribute name="selectorPool.maxSpareSelectors"
required="false">
+- <p>(int)The max spare selectors to be used in the pool, to reduce selector
contention.
+- When a selector is returned to the pool, the system can decide to keep it or let
it be GC:ed.
+- Use this option when the command line
<code>org.apache.tomcat.util.net.NioSelectorShared</code> value is set to
false.
+- Default value is <code>-1</code> (unlimited)</p>
+- </attribute>
+- <attribute name="command-line-options"
required="false">
+- <p>The following command line options are available for the NIO
connector:<br/>
+-
<code>-Dorg.apache.tomcat.util.net.NioSelectorShared=true|false</code> -
default is <code>true</code>.
+- Set this value to false if you wish to use a selector for each thread.
+- the property. If you do set it to false, you can control the size of the pool of
selectors by using the
+- selectorPool.maxSelectors attribute</p>
+- </attribute>
+- <attribute name="oomParachute" required="false">
+- <p>(int)The NIO connector implements an OutOfMemoryError strategy called
parachute.
+- It holds a chunk of data as a byte array. In case of an OOM,
+- this chunk of data is released and the error is reported. This will give the
VM enough room
+- to clean up. The <code>oomParachute</code> represent the size in
bytes of the parachute(the byte array).
+- The default value is <code>1024*1024</code>(1MB).
+- Please note, this only works for OOM errors regarding the Java Heap space,
and there is absolutely no
+- guarantee that you will be able to recover at all.
+- If you have an OOM outside of the Java Heap, then this parachute trick will
not help.
+- </p>
+- </attribute>
+- </attributes>
+ </subsection>
+
+ </section>
+Index: java/org/apache/tomcat/util/net/NioBlockingSelector.java
+===================================================================
+--- java/org/apache/tomcat/util/net/NioBlockingSelector.java (revision 590752)
++++ java/org/apache/tomcat/util/net/NioBlockingSelector.java (working copy)
+@@ -1,161 +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.EOFException;
+-import java.io.IOException;
+-import java.net.SocketTimeoutException;
+-import java.nio.ByteBuffer;
+-import java.nio.channels.SelectionKey;
+-import java.util.concurrent.TimeUnit;
+-
+-import org.apache.tomcat.util.net.NioEndpoint.KeyAttachment;
+-
+-public class NioBlockingSelector {
+- public NioBlockingSelector() {
+- }
+-
+- /**
+- * Performs a blocking write using the bytebuffer for data to be written
+- * If the <code>selector</code> parameter is null, then it will perform
a busy write that could
+- * take up a lot of CPU cycles.
+- * @param buf ByteBuffer - the buffer containing the data, we will write as long as
<code>(buf.hasRemaining()==true)</code>
+- * @param socket SocketChannel - the socket to write data to
+- * @param writeTimeout long - the timeout for this write operation in milliseconds,
-1 means no timeout
+- * @return int - returns the number of bytes written
+- * @throws EOFException if write returns -1
+- * @throws SocketTimeoutException if the write times out
+- * @throws IOException if an IO Exception occurs in the underlying socket logic
+- */
+- public static int write(ByteBuffer buf, NioChannel socket, long writeTimeout) throws
IOException {
+- SelectionKey key =
socket.getIOChannel().keyFor(socket.getPoller().getSelector());
+- int written = 0;
+- boolean timedout = false;
+- int keycount = 1; //assume we can write
+- long time = System.currentTimeMillis(); //start the timeout timer
+- try {
+- while ( (!timedout) && buf.hasRemaining()) {
+- if (keycount > 0) { //only write if we were registered for a write
+- int cnt = socket.write(buf); //write the data
+- if (cnt == -1)
+- throw new EOFException();
+- written += cnt;
+- if (cnt > 0) {
+- time = System.currentTimeMillis(); //reset our timeout timer
+- continue; //we successfully wrote, try again without a selector
+- }
+- }
+- if ( key == null ) throw new IOException("Key no longer
registered");
+- KeyAttachment att = (KeyAttachment) key.attachment();
+- try {
+- if ( att.getWriteLatch()==null || att.getWriteLatch().getCount()==0)
att.startWriteLatch(1);
+- //only register for write if a write has not yet been issued
+- if ( (att.interestOps() & SelectionKey.OP_WRITE) == 0)
socket.getPoller().add(socket,SelectionKey.OP_WRITE);
+- att.awaitWriteLatch(writeTimeout,TimeUnit.MILLISECONDS);
+- }catch (InterruptedException ignore) {
+- Thread.interrupted();
+- }
+- if ( att.getWriteLatch()!=null &&
att.getWriteLatch().getCount()> 0) {
+- //we got interrupted, but we haven't received notification from
the poller.
+- keycount = 0;
+- }else {
+- //latch countdown has happened
+- keycount = 1;
+- att.resetWriteLatch();
+- }
+-
+- if (writeTimeout > 0 && (keycount == 0))
+- timedout = (System.currentTimeMillis() - time) >= writeTimeout;
+- } //while
+- if (timedout)
+- throw new SocketTimeoutException();
+- } finally {
+- if (timedout && key != null) {
+- cancelKey(socket, key);
+- }
+- }
+- return written;
+- }
+-
+- private static void cancelKey(final NioChannel socket, final SelectionKey key) {
+- socket.getPoller().addEvent(
+- new Runnable() {
+- public void run() {
+- key.cancel();
+- }
+- });
+- }
+-
+- /**
+- * Performs a blocking read using the bytebuffer for data to be read
+- * If the <code>selector</code> parameter is null, then it will perform
a busy read that could
+- * take up a lot of CPU cycles.
+- * @param buf ByteBuffer - the buffer containing the data, we will read as until we
have read at least one byte or we timed out
+- * @param socket SocketChannel - the socket to write data to
+- * @param selector Selector - the selector to use for blocking, if null then a busy
read will be initiated
+- * @param readTimeout long - the timeout for this read operation in milliseconds, -1
means no timeout
+- * @return int - returns the number of bytes read
+- * @throws EOFException if read returns -1
+- * @throws SocketTimeoutException if the read times out
+- * @throws IOException if an IO Exception occurs in the underlying socket logic
+- */
+- public static int read(ByteBuffer buf, NioChannel socket, long readTimeout) throws
IOException {
+- final SelectionKey key =
socket.getIOChannel().keyFor(socket.getPoller().getSelector());
+- int read = 0;
+- boolean timedout = false;
+- int keycount = 1; //assume we can write
+- long time = System.currentTimeMillis(); //start the timeout timer
+- try {
+- while ( (!timedout) && read == 0) {
+- if (keycount > 0) { //only read if we were registered for a read
+- int cnt = socket.read(buf);
+- if (cnt == -1)
+- throw new EOFException();
+- read += cnt;
+- if (cnt > 0)
+- break;
+- }
+- KeyAttachment att = (KeyAttachment) key.attachment();
+- try {
+- if ( att.getReadLatch()==null || att.getReadLatch().getCount()==0)
att.startReadLatch(1);
+- if ( att.interestOps() == 0)
socket.getPoller().add(socket,SelectionKey.OP_READ);
+- att.awaitReadLatch(readTimeout,TimeUnit.MILLISECONDS);
+- }catch (InterruptedException ignore) {
+- Thread.interrupted();
+- }
+- if ( att.getReadLatch()!=null &&
att.getReadLatch().getCount()> 0) {
+- //we got interrupted, but we haven't received notification from
the poller.
+- keycount = 0;
+- }else {
+- //latch countdown has happened
+- keycount = 1;
+- att.resetReadLatch();
+- }
+- if (readTimeout > 0 && (keycount == 0))
+- timedout = (System.currentTimeMillis() - time) >= readTimeout;
+- } //while
+- if (timedout)
+- throw new SocketTimeoutException();
+- } finally {
+- if (timedout && key != null) {
+- cancelKey(socket,key);
+- }
+- }
+- return read;
+- }
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/tomcat/util/net/NioChannel.java
+===================================================================
+--- java/org/apache/tomcat/util/net/NioChannel.java (revision 590752)
++++ java/org/apache/tomcat/util/net/NioChannel.java (working copy)
+@@ -1,193 +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.nio.ByteBuffer;
+-import java.nio.channels.ByteChannel;
+-import java.nio.channels.SocketChannel;
+-
+-import org.apache.tomcat.util.net.NioEndpoint.Poller;
+-import org.apache.tomcat.util.net.SecureNioChannel.ApplicationBufferHandler;
+-import java.nio.channels.Selector;
+-import java.nio.channels.SelectionKey;
+-
+-/**
+- *
+- * Base class for a SocketChannel wrapper used by the endpoint.
+- * This way, logic for a SSL socket channel remains the same as for
+- * a non SSL, making sure we don't need to code for any exception cases.
+- *
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-public class NioChannel implements ByteChannel{
+-
+- protected static ByteBuffer emptyBuf = ByteBuffer.allocate(0);
+-
+- protected SocketChannel sc = null;
+-
+- protected ApplicationBufferHandler bufHandler;
+-
+- protected Poller poller;
+-
+- public NioChannel(SocketChannel channel, ApplicationBufferHandler bufHandler) throws
IOException {
+- this.sc = channel;
+- this.bufHandler = bufHandler;
+- }
+-
+- public void reset() throws IOException {
+- bufHandler.getReadBuffer().clear();
+- bufHandler.getWriteBuffer().clear();
+- }
+-
+- public int getBufferSize() {
+- if ( bufHandler == null ) return 0;
+- int size = 0;
+- size +=
bufHandler.getReadBuffer()!=null?bufHandler.getReadBuffer().capacity():0;
+- size +=
bufHandler.getWriteBuffer()!=null?bufHandler.getWriteBuffer().capacity():0;
+- return size;
+- }
+-
+- /**
+- * returns true if the network buffer has
+- * been flushed out and is empty
+- * @return boolean
+- */
+- public boolean flush(boolean block, Selector s,long timeout) throws IOException {
+- return true; //no network buffer in the regular channel
+- }
+-
+-
+- /**
+- * Closes this channel.
+- *
+- * @throws IOException If an I/O error occurs
+- * @todo Implement this java.nio.channels.Channel method
+- */
+- public void close() throws IOException {
+- getIOChannel().socket().close();
+- getIOChannel().close();
+- }
+-
+- public void close(boolean force) throws IOException {
+- if (isOpen() || force ) close();
+- }
+- /**
+- * Tells whether or not this channel is open.
+- *
+- * @return <tt>true</tt> if, and only if, this channel is open
+- * @todo Implement this java.nio.channels.Channel method
+- */
+- public boolean isOpen() {
+- return sc.isOpen();
+- }
+-
+- /**
+- * Writes a sequence of bytes to this channel from the given buffer.
+- *
+- * @param src The buffer from which bytes are to be retrieved
+- * @return The number of bytes written, possibly zero
+- * @throws IOException If some other I/O error occurs
+- * @todo Implement this java.nio.channels.WritableByteChannel method
+- */
+- public int write(ByteBuffer src) throws IOException {
+- return sc.write(src);
+- }
+-
+- /**
+- * Reads a sequence of bytes from this channel into the given buffer.
+- *
+- * @param dst The buffer into which bytes are to be transferred
+- * @return The number of bytes read, possibly zero, or <tt>-1</tt> if
the channel has reached end-of-stream
+- * @throws IOException If some other I/O error occurs
+- * @todo Implement this java.nio.channels.ReadableByteChannel method
+- */
+- public int read(ByteBuffer dst) throws IOException {
+- return sc.read(dst);
+- }
+-
+- public Object getAttachment(boolean remove) {
+- Poller pol = getPoller();
+- Selector sel = pol!=null?pol.getSelector():null;
+- SelectionKey key = sel!=null?getIOChannel().keyFor(sel):null;
+- Object att = key!=null?key.attachment():null;
+- if (key != null && att != null && remove ) key.attach(null);
+- return att;
+- }
+- /**
+- * getBufHandler
+- *
+- * @return ApplicationBufferHandler
+- * @todo Implement this org.apache.tomcat.util.net.SecureNioChannel method
+- */
+- public ApplicationBufferHandler getBufHandler() {
+- return bufHandler;
+- }
+-
+- public Poller getPoller() {
+- return poller;
+- }
+- /**
+- * getIOChannel
+- *
+- * @return SocketChannel
+- * @todo Implement this org.apache.tomcat.util.net.SecureNioChannel method
+- */
+- public SocketChannel getIOChannel() {
+- return sc;
+- }
+-
+- /**
+- * isClosing
+- *
+- * @return boolean
+- * @todo Implement this org.apache.tomcat.util.net.SecureNioChannel method
+- */
+- public boolean isClosing() {
+- return false;
+- }
+-
+- /**
+- * isInitHandshakeComplete
+- *
+- * @return boolean
+- * @todo Implement this org.apache.tomcat.util.net.SecureNioChannel method
+- */
+- public boolean isInitHandshakeComplete() {
+- return true;
+- }
+-
+- public int handshake(boolean read, boolean write) throws IOException {
+- return 0;
+- }
+-
+- public void setPoller(Poller poller) {
+- this.poller = poller;
+- }
+-
+- public void setIOChannel(SocketChannel IOChannel) {
+- this.sc = IOChannel;
+- }
+-
+- public String toString() {
+- return super.toString()+":"+this.sc.toString();
+- }
+-
+-}
+Index: java/org/apache/tomcat/util/net/NioSelectorPool.java
+===================================================================
+--- java/org/apache/tomcat/util/net/NioSelectorPool.java (revision 590752)
++++ java/org/apache/tomcat/util/net/NioSelectorPool.java (working copy)
+@@ -1,264 +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.util.concurrent.atomic.AtomicInteger;
+-import java.nio.channels.Selector;
+-import java.io.IOException;
+-import java.util.NoSuchElementException;
+-import java.nio.ByteBuffer;
+-import java.nio.channels.SelectionKey;
+-import java.io.EOFException;
+-import java.net.SocketTimeoutException;
+-import java.util.concurrent.ConcurrentLinkedQueue;
+-import org.apache.juli.logging.Log;
+-import org.apache.juli.logging.LogFactory;
+-
+-/**
+- *
+- * Thread safe non blocking selector pool
+- * @author Filip Hanik
+- * @version 1.0
+- * @since 6.0
+- */
+-
+-public class NioSelectorPool {
+- protected static Log log = LogFactory.getLog(NioSelectorPool.class);
+-
+- protected final static boolean SHARED =
+-
Boolean.valueOf(System.getProperty("org.apache.tomcat.util.net.NioSelectorShared",
"true")).booleanValue();
+- protected static Selector SHARED_SELECTOR;
+-
+- protected int maxSelectors = 200;
+- protected int maxSpareSelectors = -1;
+- protected boolean enabled = true;
+- protected AtomicInteger active = new AtomicInteger(0);
+- protected AtomicInteger spare = new AtomicInteger(0);
+- protected ConcurrentLinkedQueue<Selector> selectors = new
ConcurrentLinkedQueue<Selector>();
+-
+- protected static Selector getSharedSelector() throws IOException {
+- if (SHARED && SHARED_SELECTOR == null) {
+- synchronized ( NioSelectorPool.class ) {
+- if ( SHARED_SELECTOR == null ) {
+- SHARED_SELECTOR = Selector.open();
+- log.info("Using a shared selector for servlet
write/read");
+- }
+- }
+- }
+- return SHARED_SELECTOR;
+- }
+-
+- public Selector get() throws IOException{
+- if ( SHARED ) {
+- return getSharedSelector();
+- }
+- if ( (!enabled) || active.incrementAndGet() >= maxSelectors ) {
+- if ( enabled ) active.decrementAndGet();
+- return null;
+- }
+- Selector s = null;
+- try {
+- s = selectors.size()>0?selectors.poll():null;
+- if (s == null) s = Selector.open();
+- else spare.decrementAndGet();
+-
+- }catch (NoSuchElementException x ) {
+- try {s = Selector.open();}catch (IOException iox){}
+- } finally {
+- if ( s == null ) active.decrementAndGet();//we were unable to find a
selector
+- }
+- return s;
+- }
+-
+-
+-
+- public void put(Selector s) throws IOException {
+- if ( SHARED ) return;
+- if ( enabled ) active.decrementAndGet();
+- if ( enabled && (maxSpareSelectors==-1 || spare.get() <
Math.min(maxSpareSelectors,maxSelectors)) ) {
+- spare.incrementAndGet();
+- selectors.offer(s);
+- }
+- else s.close();
+- }
+-
+- public void close() throws IOException {
+- enabled = false;
+- Selector s;
+- while ( (s = selectors.poll()) != null ) s.close();
+- spare.set(0);
+- active.set(0);
+- if ( SHARED && getSharedSelector()!=null ) {
+- getSharedSelector().close();
+- SHARED_SELECTOR = null;
+- }
+- }
+-
+- public void open() throws IOException {
+- enabled = true;
+- getSharedSelector();
+- }
+-
+- /**
+- * Performs a blocking write using the bytebuffer for data to be written and a
selector to block.
+- * If the <code>selector</code> parameter is null, then it will perform
a busy write that could
+- * take up a lot of CPU cycles.
+- * @param buf ByteBuffer - the buffer containing the data, we will write as long as
<code>(buf.hasRemaining()==true)</code>
+- * @param socket SocketChannel - the socket to write data to
+- * @param selector Selector - the selector to use for blocking, if null then a busy
write will be initiated
+- * @param writeTimeout long - the timeout for this write operation in milliseconds,
-1 means no timeout
+- * @return int - returns the number of bytes written
+- * @throws EOFException if write returns -1
+- * @throws SocketTimeoutException if the write times out
+- * @throws IOException if an IO Exception occurs in the underlying socket logic
+- */
+- public int write(ByteBuffer buf, NioChannel socket, Selector selector, long
writeTimeout) throws IOException {
+- return write(buf,socket,selector,writeTimeout,true);
+- }
+-
+- public int write(ByteBuffer buf, NioChannel socket, Selector selector, long
writeTimeout, boolean block) throws IOException {
+- if ( SHARED && block) {
+- return NioBlockingSelector.write(buf,socket,writeTimeout);
+- }
+- SelectionKey key = null;
+- int written = 0;
+- boolean timedout = false;
+- int keycount = 1; //assume we can write
+- long time = System.currentTimeMillis(); //start the timeout timer
+- try {
+- while ( (!timedout) && buf.hasRemaining() ) {
+- int cnt = 0;
+- if ( keycount > 0 ) { //only write if we were registered for a write
+- cnt = socket.write(buf); //write the data
+- if (cnt == -1) throw new EOFException();
+- written += cnt;
+- if (cnt > 0) {
+- time = System.currentTimeMillis(); //reset our timeout timer
+- continue; //we successfully wrote, try again without a selector
+- }
+- if (cnt==0 && (!block)) break; //don't block
+- }
+- if ( selector != null ) {
+- //register OP_WRITE to the selector
+- if (key==null) key = socket.getIOChannel().register(selector,
SelectionKey.OP_WRITE);
+- else key.interestOps(SelectionKey.OP_WRITE);
+- keycount = selector.select(writeTimeout);
+- }
+- if (writeTimeout > 0 && (selector == null || keycount == 0) )
timedout = (System.currentTimeMillis()-time)>=writeTimeout;
+- }//while
+- if ( timedout ) throw new SocketTimeoutException();
+- } finally {
+- if (key != null) {
+- key.cancel();
+- if (selector != null) selector.selectNow();//removes the key from this
selector
+- }
+- }
+- return written;
+- }
+-
+- /**
+- * Performs a blocking read using the bytebuffer for data to be read and a selector
to block.
+- * If the <code>selector</code> parameter is null, then it will perform
a busy read that could
+- * take up a lot of CPU cycles.
+- * @param buf ByteBuffer - the buffer containing the data, we will read as until we
have read at least one byte or we timed out
+- * @param socket SocketChannel - the socket to write data to
+- * @param selector Selector - the selector to use for blocking, if null then a busy
read will be initiated
+- * @param readTimeout long - the timeout for this read operation in milliseconds, -1
means no timeout
+- * @return int - returns the number of bytes read
+- * @throws EOFException if read returns -1
+- * @throws SocketTimeoutException if the read times out
+- * @throws IOException if an IO Exception occurs in the underlying socket logic
+- */
+- public int read(ByteBuffer buf, NioChannel socket, Selector selector, long
readTimeout) throws IOException {
+- return read(buf,socket,selector,readTimeout,true);
+- }
+- /**
+- * Performs a read using the bytebuffer for data to be read and a selector to
register for events should
+- * you have the block=true.
+- * If the <code>selector</code> parameter is null, then it will perform
a busy read that could
+- * take up a lot of CPU cycles.
+- * @param buf ByteBuffer - the buffer containing the data, we will read as until we
have read at least one byte or we timed out
+- * @param socket SocketChannel - the socket to write data to
+- * @param selector Selector - the selector to use for blocking, if null then a busy
read will be initiated
+- * @param readTimeout long - the timeout for this read operation in milliseconds, -1
means no timeout
+- * @param block - true if you want to block until data becomes available or timeout
time has been reached
+- * @return int - returns the number of bytes read
+- * @throws EOFException if read returns -1
+- * @throws SocketTimeoutException if the read times out
+- * @throws IOException if an IO Exception occurs in the underlying socket logic
+- */
+- public int read(ByteBuffer buf, NioChannel socket, Selector selector, long
readTimeout, boolean block) throws IOException {
+- if ( SHARED && block) {
+- return NioBlockingSelector.read(buf,socket,readTimeout);
+- }
+- SelectionKey key = null;
+- int read = 0;
+- boolean timedout = false;
+- int keycount = 1; //assume we can write
+- long time = System.currentTimeMillis(); //start the timeout timer
+- try {
+- while ( (!timedout) ) {
+- int cnt = 0;
+- if ( keycount > 0 ) { //only read if we were registered for a read
+- cnt = socket.read(buf);
+- if (cnt == -1) throw new EOFException();
+- read += cnt;
+- if (cnt > 0) continue; //read some more
+- if (cnt==0 && (read>0 || (!block) ) ) break; //we are
done reading
+- }
+- if ( selector != null ) {//perform a blocking read
+- //register OP_WRITE to the selector
+- if (key==null) key = socket.getIOChannel().register(selector,
SelectionKey.OP_READ);
+- else key.interestOps(SelectionKey.OP_READ);
+- keycount = selector.select(readTimeout);
+- }
+- if (readTimeout > 0 && (selector == null || keycount == 0) )
timedout = (System.currentTimeMillis()-time)>=readTimeout;
+- }//while
+- if ( timedout ) throw new SocketTimeoutException();
+- } finally {
+- if (key != null) {
+- key.cancel();
+- if (selector != null) selector.selectNow();//removes the key from this
selector
+- }
+- }
+- return read;
+- }
+-
+- public void setMaxSelectors(int maxSelectors) {
+- this.maxSelectors = maxSelectors;
+- }
+-
+- public void setMaxSpareSelectors(int maxSpareSelectors) {
+- this.maxSpareSelectors = maxSpareSelectors;
+- }
+-
+- public void setEnabled(boolean enabled) {
+- this.enabled = enabled;
+- }
+-
+- public int getMaxSelectors() {
+- return maxSelectors;
+- }
+-
+- public int getMaxSpareSelectors() {
+- return maxSpareSelectors;
+- }
+-
+- public boolean isEnabled() {
+- return enabled;
+- }
+-}
+\ No newline at end of file
+Index: java/org/apache/tomcat/util/net/SecureNioChannel.java
+===================================================================
+--- java/org/apache/tomcat/util/net/SecureNioChannel.java (revision 590752)
++++ java/org/apache/tomcat/util/net/SecureNioChannel.java (working copy)
+@@ -1,473 +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.nio.ByteBuffer;
+-import java.nio.channels.SelectionKey;
+-import java.nio.channels.SocketChannel;
+-import javax.net.ssl.SSLEngine;
+-import javax.net.ssl.SSLEngineResult;
+-import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+-import javax.net.ssl.SSLEngineResult.Status;
+-import java.nio.channels.Selector;
+-
+-/**
+- *
+- * Implementation of a secure socket channel
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-
+-public class SecureNioChannel extends NioChannel {
+-
+- protected ByteBuffer netInBuffer;
+- protected ByteBuffer netOutBuffer;
+-
+- protected SSLEngine sslEngine;
+-
+- protected boolean initHandshakeComplete = false;
+- protected HandshakeStatus initHandshakeStatus; //gets set by begin handshake
+-
+- protected boolean closed = false;
+- protected boolean closing = false;
+-
+- protected NioSelectorPool pool;
+-
+- public SecureNioChannel(SocketChannel channel, SSLEngine engine,
+- ApplicationBufferHandler bufHandler, NioSelectorPool pool)
throws IOException {
+- super(channel,bufHandler);
+- this.sslEngine = engine;
+- int appBufSize = sslEngine.getSession().getApplicationBufferSize();
+- int netBufSize = sslEngine.getSession().getPacketBufferSize();
+- //allocate network buffers - TODO, add in optional direct non-direct buffers
+- if ( netInBuffer == null ) netInBuffer = ByteBuffer.allocateDirect(netBufSize);
+- if ( netOutBuffer == null ) netOutBuffer =
ByteBuffer.allocateDirect(netBufSize);
+-
+- //selector pool for blocking operations
+- this.pool = pool;
+-
+- //ensure that the application has a large enough read/write buffers
+- //by doing this, we should not encounter any buffer overflow errors
+- bufHandler.expand(bufHandler.getReadBuffer(), appBufSize);
+- bufHandler.expand(bufHandler.getWriteBuffer(), appBufSize);
+- reset();
+- }
+-
+- public void reset(SSLEngine engine) throws IOException {
+- this.sslEngine = engine;
+- reset();
+- }
+- public void reset() throws IOException {
+- super.reset();
+- netOutBuffer.position(0);
+- netOutBuffer.limit(0);
+- netInBuffer.position(0);
+- netInBuffer.limit(0);
+- initHandshakeComplete = false;
+- closed = false;
+- closing = false;
+- //initiate handshake
+- sslEngine.beginHandshake();
+- initHandshakeStatus = sslEngine.getHandshakeStatus();
+- }
+-
+- public int getBufferSize() {
+- int size = super.getBufferSize();
+- size += netInBuffer!=null?netInBuffer.capacity():0;
+- size += netOutBuffer!=null?netOutBuffer.capacity():0;
+- return size;
+- }
+-
+-
+-//===========================================================================================
+-// NIO SSL METHODS
+-//===========================================================================================
+- /**
+- * returns true if the network buffer has
+- * been flushed out and is empty
+- * @return boolean
+- */
+- public boolean flush(boolean block, Selector s, long timeout) throws IOException {
+- if (!block) {
+- flush(netOutBuffer);
+- } else {
+- pool.write(netOutBuffer, this, s, timeout);
+- }
+- return !netOutBuffer.hasRemaining();
+- }
+-
+- /**
+- * Flushes the buffer to the network, non blocking
+- * @param buf ByteBuffer
+- * @return boolean true if the buffer has been emptied out, false otherwise
+- * @throws IOException
+- */
+- protected boolean flush(ByteBuffer buf) throws IOException {
+- int remaining = buf.remaining();
+- if ( remaining > 0 ) {
+- int written = sc.write(buf);
+- return written >= remaining;
+- }else {
+- return true;
+- }
+- }
+-
+- /**
+- * Performs SSL handshake, non blocking, but performs NEED_TASK on the same
thread.<br>
+- * Hence, you should never call this method using your Acceptor thread, as you would
slow down
+- * your system significantly.<br>
+- * The return for this operation is 0 if the handshake is complete and a positive
value if it is not complete.
+- * In the event of a positive value coming back, reregister the selection key for
the return values interestOps.
+- * @param read boolean - true if the underlying channel is readable
+- * @param write boolean - true if the underlying channel is writable
+- * @return int - 0 if hand shake is complete, otherwise it returns a SelectionKey
interestOps value
+- * @throws IOException
+- */
+- public int handshake(boolean read, boolean write) throws IOException {
+- if ( initHandshakeComplete ) return 0; //we have done our initial handshake
+-
+- if (!flush(netOutBuffer)) return SelectionKey.OP_WRITE; //we still have data to
write
+-
+- SSLEngineResult handshake = null;
+-
+- while (!initHandshakeComplete) {
+- switch ( initHandshakeStatus ) {
+- case NOT_HANDSHAKING: {
+- //should never happen
+- throw new IOException("NOT_HANDSHAKING during
handshake");
+- }
+- case FINISHED: {
+- //we are complete if we have delivered the last package
+- initHandshakeComplete = !netOutBuffer.hasRemaining();
+- //return 0 if we are complete, otherwise we still have data to
write
+- return initHandshakeComplete?0:SelectionKey.OP_WRITE;
+- }
+- case NEED_WRAP: {
+- //perform the wrap function
+- handshake = handshakeWrap(write);
+- if ( handshake.getStatus() == Status.OK ){
+- if (initHandshakeStatus == HandshakeStatus.NEED_TASK)
+- initHandshakeStatus = tasks();
+- } else {
+- //wrap should always work with our buffers
+- throw new IOException("Unexpected status:" +
handshake.getStatus() + " during handshake WRAP.");
+- }
+- if ( initHandshakeStatus != HandshakeStatus.NEED_UNWRAP ||
(!flush(netOutBuffer)) ) {
+- //should actually return OP_READ if we have NEED_UNWRAP
+- return SelectionKey.OP_WRITE;
+- }
+- //fall down to NEED_UNWRAP on the same call, will result in a
+- //BUFFER_UNDERFLOW if it needs data
+- }
+- case NEED_UNWRAP: {
+- //perform the unwrap function
+- handshake = handshakeUnwrap(read);
+- if ( handshake.getStatus() == Status.OK ) {
+- if (initHandshakeStatus == HandshakeStatus.NEED_TASK)
+- initHandshakeStatus = tasks();
+- } else if ( handshake.getStatus() == Status.BUFFER_UNDERFLOW ){
+- //read more data, reregister for OP_READ
+- return SelectionKey.OP_READ;
+- } else {
+- throw new IOException("Invalid handshake
status:"+initHandshakeStatus+" during handshake UNWRAP.");
+- }//switch
+- break;
+- }
+- case NEED_TASK: {
+- initHandshakeStatus = tasks();
+- break;
+- }
+- default: throw new IllegalStateException("Invalid handshake
status:"+initHandshakeStatus);
+- }//switch
+- }//while
+- //return 0 if we are complete, otherwise reregister for any activity that
+- //would cause this method to be called again.
+- return initHandshakeComplete?0:(SelectionKey.OP_WRITE|SelectionKey.OP_READ);
+- }
+-
+- /**
+- * Executes all the tasks needed on the same thread.
+- * @return HandshakeStatus
+- */
+- protected SSLEngineResult.HandshakeStatus tasks() {
+- Runnable r = null;
+- while ( (r = sslEngine.getDelegatedTask()) != null) {
+- r.run();
+- }
+- return sslEngine.getHandshakeStatus();
+- }
+-
+- /**
+- * Performs the WRAP function
+- * @param doWrite boolean
+- * @return SSLEngineResult
+- * @throws IOException
+- */
+- protected SSLEngineResult handshakeWrap(boolean doWrite) throws IOException {
+- //this should never be called with a network buffer that contains data
+- //so we can clear it here.
+- netOutBuffer.clear();
+- //perform the wrap
+- SSLEngineResult result = sslEngine.wrap(bufHandler.getWriteBuffer(),
netOutBuffer);
+- //prepare the results to be written
+- netOutBuffer.flip();
+- //set the status
+- initHandshakeStatus = result.getHandshakeStatus();
+- //optimization, if we do have a writable channel, write it now
+- if ( doWrite ) flush(netOutBuffer);
+- return result;
+- }
+-
+- /**
+- * Perform handshake unwrap
+- * @param doread boolean
+- * @return SSLEngineResult
+- * @throws IOException
+- */
+- protected SSLEngineResult handshakeUnwrap(boolean doread) throws IOException {
+-
+- if (netInBuffer.position() == netInBuffer.limit()) {
+- //clear the buffer if we have emptied it out on data
+- netInBuffer.clear();
+- }
+- if ( doread ) {
+- //if we have data to read, read it
+- int read = sc.read(netInBuffer);
+- if (read == -1) throw new IOException("EOF encountered during
handshake.");
+- }
+- SSLEngineResult result;
+- boolean cont = false;
+- //loop while we can perform pure SSLEngine data
+- do {
+- //prepare the buffer with the incoming data
+- netInBuffer.flip();
+- //call unwrap
+- result = sslEngine.unwrap(netInBuffer, bufHandler.getReadBuffer());
+- //compact the buffer, this is an optional method, wonder what would happen
if we didn't
+- netInBuffer.compact();
+- //read in the status
+- initHandshakeStatus = result.getHandshakeStatus();
+- if ( result.getStatus() == SSLEngineResult.Status.OK &&
+- result.getHandshakeStatus() == HandshakeStatus.NEED_TASK ) {
+- //execute tasks if we need to
+- initHandshakeStatus = tasks();
+- }
+- //perform another unwrap?
+- cont = result.getStatus() == SSLEngineResult.Status.OK &&
+- initHandshakeStatus == HandshakeStatus.NEED_UNWRAP;
+- }while ( cont );
+- return result;
+- }
+-
+- /**
+- * Sends a SSL close message, will not physically close the connection
here.<br>
+- * To close the connection, you could do something like
+- * <pre><code>
+- * close();
+- * while (isOpen() && !myTimeoutFunction()) Thread.sleep(25);
+- * if ( isOpen() ) close(true); //forces a close if you timed out
+- * </code></pre>
+- * @throws IOException if an I/O error occurs
+- * @throws IOException if there is data on the outgoing network buffer and we are
unable to flush it
+- * @todo Implement this java.io.Closeable method
+- */
+- public void close() throws IOException {
+- if (closing) return;
+- closing = true;
+- sslEngine.closeOutbound();
+-
+- if (!flush(netOutBuffer)) {
+- throw new IOException("Remaining data in the network buffer, can't
send SSL close message, force a close with close(true) instead");
+- }
+- //prep the buffer for the close message
+- netOutBuffer.clear();
+- //perform the close, since we called sslEngine.closeOutbound
+- SSLEngineResult handshake = sslEngine.wrap(getEmptyBuf(), netOutBuffer);
+- //we should be in a close state
+- if (handshake.getStatus() != SSLEngineResult.Status.CLOSED) {
+- throw new IOException("Invalid close state, will not send network
data.");
+- }
+- //prepare the buffer for writing
+- netOutBuffer.flip();
+- //if there is data to be written
+- flush(netOutBuffer);
+-
+- //is the channel closed?
+- closed = (!netOutBuffer.hasRemaining() &&
(handshake.getHandshakeStatus() != HandshakeStatus.NEED_WRAP));
+- }
+-
+- /**
+- * Force a close, can throw an IOException
+- * @param force boolean
+- * @throws IOException
+- */
+- public void close(boolean force) throws IOException {
+- try {
+- close();
+- }finally {
+- if ( force || closed ) {
+- closed = true;
+- sc.socket().close();
+- sc.close();
+- }
+- }
+- }
+-
+- /**
+- * Reads a sequence of bytes from this channel into the given buffer.
+- *
+- * @param dst The buffer into which bytes are to be transferred
+- * @return The number of bytes read, possibly zero, or <tt>-1</tt> if
the channel has reached end-of-stream
+- * @throws IOException If some other I/O error occurs
+- * @throws IllegalArgumentException if the destination buffer is different than
bufHandler.getReadBuffer()
+- * @todo Implement this java.nio.channels.ReadableByteChannel method
+- */
+- public int read(ByteBuffer dst) throws IOException {
+- //if we want to take advantage of the expand function, make sure we only use the
ApplicationBufferHandler's buffers
+- if ( dst != bufHandler.getReadBuffer() ) throw new
IllegalArgumentException("You can only read using the application read buffer
provided by the handler.");
+- //are we in the middle of closing or closed?
+- if ( closing || closed) return -1;
+- //did we finish our handshake?
+- if (!initHandshakeComplete) throw new IllegalStateException("Handshake
incomplete, you must complete handshake before reading data.");
+-
+- //read from the network
+- int netread = sc.read(netInBuffer);
+- //did we reach EOF? if so send EOF up one layer.
+- if (netread == -1) return -1;
+-
+- //the data read
+- int read = 0;
+- //the SSL engine result
+- SSLEngineResult unwrap;
+- do {
+- //prepare the buffer
+- netInBuffer.flip();
+- //unwrap the data
+- unwrap = sslEngine.unwrap(netInBuffer, dst);
+- //compact the buffer
+- netInBuffer.compact();
+-
+- if ( unwrap.getStatus()==Status.OK ||
unwrap.getStatus()==Status.BUFFER_UNDERFLOW ) {
+- //we did receive some data, add it to our total
+- read += unwrap.bytesProduced();
+- //perform any tasks if needed
+- if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_TASK) tasks();
+- //if we need more network data, then bail out for now.
+- if ( unwrap.getStatus() == Status.BUFFER_UNDERFLOW ) break;
+- }else if ( unwrap.getStatus()==Status.BUFFER_OVERFLOW && read>0 )
{
+- //buffer overflow can happen, if we have read data, then
+- //empty out the dst buffer before we do another read
+- break;
+- }else {
+- //here we should trap BUFFER_OVERFLOW and call expand on the buffer
+- //for now, throw an exception, as we initialized the buffers
+- //in the constructor
+- throw new IOException("Unable to unwrap data, invalid status:
" + unwrap.getStatus());
+- }
+- } while ( (netInBuffer.position() != 0)); //continue to unwrapping as long as
the input buffer has stuff
+- return (read);
+- }
+-
+- /**
+- * Writes a sequence of bytes to this channel from the given buffer.
+- *
+- * @param src The buffer from which bytes are to be retrieved
+- * @return The number of bytes written, possibly zero
+- * @throws IOException If some other I/O error occurs
+- * @todo Implement this java.nio.channels.WritableByteChannel method
+- */
+- public int write(ByteBuffer src) throws IOException {
+- if ( src == this.netOutBuffer ) {
+- //we can get here through a recursive call
+- //by using the NioBlockingSelector
+- int written = sc.write(src);
+- return written;
+- } else {
+- //make sure we can handle expand, and that we only use on buffer
+- if ( src != bufHandler.getWriteBuffer() ) throw new
IllegalArgumentException("You can only write using the application write buffer
provided by the handler.");
+- //are we closing or closed?
+- if ( closing || closed) throw new IOException("Channel is in closing
state.");
+-
+- //the number of bytes written
+- int written = 0;
+-
+- if (!flush(netOutBuffer)) {
+- //we haven't emptied out the buffer yet
+- return written;
+- }
+-
+- /*
+- * The data buffer is empty, we can reuse the entire buffer.
+- */
+- netOutBuffer.clear();
+-
+- SSLEngineResult result = sslEngine.wrap(src, netOutBuffer);
+- written = result.bytesConsumed();
+- netOutBuffer.flip();
+-
+- if (result.getStatus() == Status.OK) {
+- if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) tasks();
+- } else {
+- throw new IOException("Unable to wrap data, invalid engine state:
" +result.getStatus());
+- }
+-
+- //force a flush
+- flush(netOutBuffer);
+- return written;
+- }
+- }
+-
+- /**
+- * Callback interface to be able to expand buffers
+- * when buffer overflow exceptions happen
+- */
+- public static interface ApplicationBufferHandler {
+- public ByteBuffer expand(ByteBuffer buffer, int remaining);
+- public ByteBuffer getReadBuffer();
+- public ByteBuffer getWriteBuffer();
+- }
+-
+- public ApplicationBufferHandler getBufHandler() {
+- return bufHandler;
+- }
+-
+- public boolean isInitHandshakeComplete() {
+- return initHandshakeComplete;
+- }
+-
+- public boolean isClosing() {
+- return closing;
+- }
+-
+- public SSLEngine getSslEngine() {
+- return sslEngine;
+- }
+-
+- public ByteBuffer getEmptyBuf() {
+- return emptyBuf;
+- }
+-
+- public void setBufHandler(ApplicationBufferHandler bufHandler) {
+- this.bufHandler = bufHandler;
+- }
+-
+- public SocketChannel getIOChannel() {
+- return sc;
+- }
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/tomcat/util/net/NioEndpoint.java
+===================================================================
+--- java/org/apache/tomcat/util/net/NioEndpoint.java (revision 590752)
++++ java/org/apache/tomcat/util/net/NioEndpoint.java (working copy)
+@@ -1,2197 +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.File;
+-import java.io.FileInputStream;
+-import java.io.IOException;
+-import java.net.InetAddress;
+-import java.net.InetSocketAddress;
+-import java.net.Socket;
+-import java.nio.ByteBuffer;
+-import java.nio.channels.CancelledKeyException;
+-import java.nio.channels.FileChannel;
+-import java.nio.channels.SelectionKey;
+-import java.nio.channels.Selector;
+-import java.nio.channels.ServerSocketChannel;
+-import java.nio.channels.SocketChannel;
+-import java.security.KeyStore;
+-import java.util.Collection;
+-import java.util.Iterator;
+-import java.util.Set;
+-import java.util.StringTokenizer;
+-import java.util.concurrent.ConcurrentLinkedQueue;
+-import java.util.concurrent.CountDownLatch;
+-import java.util.concurrent.Executor;
+-import java.util.concurrent.LinkedBlockingQueue;
+-import java.util.concurrent.ThreadFactory;
+-import java.util.concurrent.ThreadPoolExecutor;
+-import java.util.concurrent.TimeUnit;
+-import java.util.concurrent.atomic.AtomicInteger;
+-import java.util.concurrent.atomic.AtomicLong;
+-import javax.net.ssl.KeyManagerFactory;
+-import javax.net.ssl.SSLContext;
+-import javax.net.ssl.SSLEngine;
+-import javax.net.ssl.TrustManagerFactory;
+-
+-import org.apache.juli.logging.Log;
+-import org.apache.juli.logging.LogFactory;
+-import org.apache.tomcat.util.IntrospectionUtils;
+-import org.apache.tomcat.util.net.SecureNioChannel.ApplicationBufferHandler;
+-import org.apache.tomcat.util.res.StringManager;
+-
+-/**
+- * NIO tailored thread pool, providing the following services:
+- * <ul>
+- * <li>Socket acceptor thread</li>
+- * <li>Socket poller thread</li>
+- * <li>Worker threads pool</li>
+- * </ul>
+- *
+- * When switching to Java 5, there's an opportunity to use the virtual
+- * machine's thread pool.
+- *
+- * @author Mladen Turk
+- * @author Remy Maucherat
+- * @author Filip Hanik
+- */
+-public class NioEndpoint {
+-
+-
+- // -------------------------------------------------------------- Constants
+-
+-
+- protected static Log log = LogFactory.getLog(NioEndpoint.class);
+-
+- protected static StringManager sm =
+- StringManager.getManager("org.apache.tomcat.util.net.res");
+-
+-
+- /**
+- * The Request attribute key for the cipher suite.
+- */
+- public static final String CIPHER_SUITE_KEY =
"javax.servlet.request.cipher_suite";
+-
+- /**
+- * The Request attribute key for the key size.
+- */
+- public static final String KEY_SIZE_KEY =
"javax.servlet.request.key_size";
+-
+- /**
+- * The Request attribute key for the client certificate chain.
+- */
+- public static final String CERTIFICATE_KEY =
"javax.servlet.request.X509Certificate";
+-
+- /**
+- * The Request attribute key for the session id.
+- * This one is a Tomcat extension to the Servlet spec.
+- */
+- public static final String SESSION_ID_KEY =
"javax.servlet.request.ssl_session";
+-
+- public static final int OP_REGISTER = -1; //register interest op
+-
+- // ----------------------------------------------------------------- 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;
+-
+- protected NioSelectorPool selectorPool = new NioSelectorPool();
+-
+- /**
+- * Server socket "pointer".
+- */
+- protected ServerSocketChannel serverSock = null;
+-
+- /**
+- * use send file
+- */
+- protected boolean useSendfile = true;
+-
+- /**
+- * The size of the OOM parachute.
+- */
+- protected int oomParachute = 1024*1024;
+- /**
+- * The oom parachute, when an OOM error happens,
+- * will release the data, giving the JVM instantly
+- * a chunk of data to be able to recover with.
+- */
+- protected byte[] oomParachuteData = null;
+-
+- /**
+- * Make sure this string has already been allocated
+- */
+- protected static final String oomParachuteMsg =
+- "SEVERE:Memory usage is low, parachute is non existent, your system may
start failing.";
+-
+- /**
+- * Keep track of OOM warning messages.
+- */
+- long lastParachuteCheck = System.currentTimeMillis();
+-
+-
+-
+-
+-
+- /**
+- * Cache for SocketProcessor objects
+- */
+- protected ConcurrentLinkedQueue<SocketProcessor> processorCache = new
ConcurrentLinkedQueue<SocketProcessor>() {
+- protected AtomicInteger size = new AtomicInteger(0);
+- public boolean offer(SocketProcessor sc) {
+- sc.reset(null,null);
+- boolean offer =
socketProperties.getProcessorCache()==-1?true:size.get()<socketProperties.getProcessorCache();
+- //avoid over growing our cache or add after we have stopped
+- if ( running && (!paused) && (offer) ) {
+- boolean result = super.offer(sc);
+- if ( result ) {
+- size.incrementAndGet();
+- }
+- return result;
+- }
+- else return false;
+- }
+-
+- public SocketProcessor poll() {
+- SocketProcessor result = super.poll();
+- if ( result != null ) {
+- size.decrementAndGet();
+- }
+- return result;
+- }
+-
+- public void clear() {
+- super.clear();
+- size.set(0);
+- }
+- };
+-
+-
+- /**
+- * Cache for key attachment objects
+- */
+- protected ConcurrentLinkedQueue<KeyAttachment> keyCache = new
ConcurrentLinkedQueue<KeyAttachment>() {
+- protected AtomicInteger size = new AtomicInteger(0);
+- public boolean offer(KeyAttachment ka) {
+- ka.reset();
+- boolean offer =
socketProperties.getKeyCache()==-1?true:size.get()<socketProperties.getKeyCache();
+- //avoid over growing our cache or add after we have stopped
+- if ( running && (!paused) && (offer) ) {
+- boolean result = super.offer(ka);
+- if ( result ) {
+- size.incrementAndGet();
+- }
+- return result;
+- }
+- else return false;
+- }
+-
+- public KeyAttachment poll() {
+- KeyAttachment result = super.poll();
+- if ( result != null ) {
+- size.decrementAndGet();
+- }
+- return result;
+- }
+-
+- public void clear() {
+- super.clear();
+- size.set(0);
+- }
+- };
+-
+-
+- /**
+- * Cache for poller events
+- */
+- protected ConcurrentLinkedQueue<PollerEvent> eventCache = new
ConcurrentLinkedQueue<PollerEvent>() {
+- protected AtomicInteger size = new AtomicInteger(0);
+- public boolean offer(PollerEvent pe) {
+- pe.reset();
+- boolean offer =
socketProperties.getEventCache()==-1?true:size.get()<socketProperties.getEventCache();
+- //avoid over growing our cache or add after we have stopped
+- if ( running && (!paused) && (offer) ) {
+- boolean result = super.offer(pe);
+- if ( result ) {
+- size.incrementAndGet();
+- }
+- return result;
+- }
+- else return false;
+- }
+-
+- public PollerEvent poll() {
+- PollerEvent result = super.poll();
+- if ( result != null ) {
+- size.decrementAndGet();
+- }
+- return result;
+- }
+-
+- public void clear() {
+- super.clear();
+- size.set(0);
+- }
+- };
+-
+-
+- /**
+- * Bytebuffer cache, each channel holds a set of buffers (two, except for SSL holds
four)
+- */
+- protected ConcurrentLinkedQueue<NioChannel> nioChannels = new
ConcurrentLinkedQueue<NioChannel>() {
+- protected AtomicInteger size = new AtomicInteger(0);
+- protected AtomicInteger bytes = new AtomicInteger(0);
+- public boolean offer(NioChannel socket) {
+- boolean offer =
socketProperties.getBufferPool()==-1?true:size.get()<socketProperties.getBufferPool();
+- offer = offer &&
(socketProperties.getBufferPoolSize()==-1?true:(bytes.get()+socket.getBufferSize())<socketProperties.getBufferPoolSize());
+- //avoid over growing our cache or add after we have stopped
+- if ( running && (!paused) && (offer) ) {
+- boolean result = super.offer(socket);
+- if ( result ) {
+- size.incrementAndGet();
+- bytes.addAndGet(socket.getBufferSize());
+- }
+- return result;
+- }
+- else return false;
+- }
+-
+- public NioChannel poll() {
+- NioChannel result = super.poll();
+- if ( result != null ) {
+- size.decrementAndGet();
+- bytes.addAndGet(-result.getBufferSize());
+- }
+- return result;
+- }
+-
+- public void clear() {
+- super.clear();
+- size.set(0);
+- bytes.set(0);
+- }
+- };
+-
+-
+-
+- // ------------------------------------------------------------- Properties
+-
+-
+- /**
+- * External Executor based thread pool.
+- */
+- protected Executor executor = null;
+- public void setExecutor(Executor executor) { this.executor = executor; }
+- public Executor getExecutor() { return executor; }
+-
+- protected boolean useExecutor = true;
+- public void setUseExecutor(boolean useexec) { useExecutor = useexec;}
+- public boolean getUseExecutor() { return useExecutor || (executor!=null);}
+-
+- /**
+- * Maximum amount of worker threads.
+- */
+- protected int maxThreads = 400;
+- public void setMaxThreads(int maxThreads) { this.maxThreads = maxThreads; }
+- public int getMaxThreads() { return maxThreads; }
+-
+-
+- /**
+- * Priority of the worker threads.
+- */
+- protected int threadPriority = Thread.NORM_PRIORITY;
+- public void setThreadPriority(int threadPriority) { this.threadPriority =
threadPriority; }
+- public int getThreadPriority() { return threadPriority; }
+-
+- /**
+- * Priority of the acceptor threads.
+- */
+- protected int acceptorThreadPriority = Thread.NORM_PRIORITY;
+- public void setAcceptorThreadPriority(int acceptorThreadPriority) {
this.acceptorThreadPriority = acceptorThreadPriority; }
+- public int getAcceptorThreadPriority() { return acceptorThreadPriority; }
+-
+- /**
+- * Priority of the poller threads.
+- */
+- protected int pollerThreadPriority = Thread.NORM_PRIORITY;
+- public void setPollerThreadPriority(int pollerThreadPriority) {
this.pollerThreadPriority = pollerThreadPriority; }
+- public int getPollerThreadPriority() { return pollerThreadPriority; }
+-
+- /**
+- * 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; }
+-
+- protected SocketProperties socketProperties = new SocketProperties();
+-
+- /**
+- * Socket TCP no delay.
+- */
+- public boolean getTcpNoDelay() { return socketProperties.getTcpNoDelay();}
+- public void setTcpNoDelay(boolean tcpNoDelay) {
socketProperties.setTcpNoDelay(tcpNoDelay); }
+-
+-
+- /**
+- * Socket linger.
+- */
+- public int getSoLinger() { return socketProperties.getSoLingerTime(); }
+- public void setSoLinger(int soLinger) {
+- socketProperties.setSoLingerTime(soLinger);
+- socketProperties.setSoLingerOn(soLinger>=0);
+- }
+-
+-
+- /**
+- * Socket timeout.
+- */
+- public int getSoTimeout() { return socketProperties.getSoTimeout(); }
+- public void setSoTimeout(int soTimeout) { socketProperties.setSoTimeout(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; }
+-
+-
+-
+- /**
+- * Allow comet request handling.
+- */
+- protected boolean useComet = true;
+- public void setUseComet(boolean useComet) { this.useComet = useComet; }
+- public boolean getUseComet() { return useComet; }
+-
+-
+- /**
+- * Acceptor thread count.
+- */
+- protected int acceptorThreadCount = 1;
+- public void setAcceptorThreadCount(int acceptorThreadCount) {
this.acceptorThreadCount = acceptorThreadCount; }
+- public int getAcceptorThreadCount() { return acceptorThreadCount; }
+-
+-
+-
+- /**
+- * Poller thread count.
+- */
+- protected int pollerThreadCount = 1;
+- public void setPollerThreadCount(int pollerThreadCount) { this.pollerThreadCount =
pollerThreadCount; }
+- public int getPollerThreadCount() { return pollerThreadCount; }
+-
+- protected long selectorTimeout = 1000;
+- public void setSelectorTimeout(long timeout){ this.selectorTimeout = timeout;}
+- public long getSelectorTimeout(){ return this.selectorTimeout; }
+- /**
+- * The socket poller.
+- */
+- protected Poller[] pollers = null;
+- protected int pollerRoundRobin = 0;
+- public Poller getPoller0() {
+- pollerRoundRobin = (pollerRoundRobin + 1) % pollers.length;
+- Poller poller = pollers[pollerRoundRobin];
+- return poller;
+- }
+-
+-
+- /**
+- * The socket poller used for Comet support.
+- */
+- public Poller getCometPoller0() {
+- Poller poller = getPoller0();
+- return poller;
+- }
+-
+-
+- /**
+- * Dummy maxSpareThreads property.
+- */
+- public int getMaxSpareThreads() { return Math.min(getMaxThreads(),5); }
+-
+-
+- /**
+- * Dummy minSpareThreads property.
+- */
+- public int getMinSpareThreads() { return Math.min(getMaxThreads(),5); }
+-
+- /**
+- * Generic properties, introspected
+- */
+- public boolean setProperty(String name, String value) {
+- final String selectorPoolName = "selectorPool.";
+- final String socketName = "socket.";
+- try {
+- if (name.startsWith(selectorPoolName)) {
+- return IntrospectionUtils.setProperty(selectorPool,
name.substring(selectorPoolName.length()), value);
+- } else if (name.startsWith(socketName)) {
+- return IntrospectionUtils.setProperty(socketProperties,
name.substring(socketName.length()), value);
+- } else {
+- return IntrospectionUtils.setProperty(this,name,value);
+- }
+- }catch ( Exception x ) {
+- log.error("Unable to set attribute \""+name+"\" to
\""+value+"\"",x);
+- return false;
+- }
+- }
+-
+-
+- public String adjustRelativePath(String path, String relativeTo) {
+- File f = new File(path);
+- if ( !f.isAbsolute()) {
+- path = relativeTo + File.separator + path;
+- f = new File(path);
+- }
+- if (!f.exists()) {
+- log.warn("configured file:["+path+"] does not exist.");
+- }
+- return path;
+- }
+-
+- public String defaultIfNull(String val, String defaultValue) {
+- if (val==null) return defaultValue;
+- else return val;
+- }
+- // -------------------- SSL related properties --------------------
+- protected String truststoreFile =
System.getProperty("javax.net.ssl.trustStore");
+- public void setTruststoreFile(String s) {
+- s = adjustRelativePath(s,System.getProperty("catalina.base"));
+- this.truststoreFile = s;
+- }
+- public String getTruststoreFile() {return truststoreFile;}
+- protected String truststorePass =
System.getProperty("javax.net.ssl.trustStorePassword");
+- public void setTruststorePass(String truststorePass) {this.truststorePass =
truststorePass;}
+- public String getTruststorePass() {return truststorePass;}
+- protected String truststoreType =
System.getProperty("javax.net.ssl.trustStoreType");
+- public void setTruststoreType(String truststoreType) {this.truststoreType =
truststoreType;}
+- public String getTruststoreType() {return truststoreType;}
+-
+- protected String keystoreFile =
System.getProperty("user.home")+"/.keystore";
+- public String getKeystoreFile() { return keystoreFile;}
+- public void setKeystoreFile(String s ) {
+- s = adjustRelativePath(s,System.getProperty("catalina.base"));
+- this.keystoreFile = s;
+- }
+-
+- protected String algorithm = "SunX509";
+- public String getAlgorithm() { return algorithm;}
+- public void setAlgorithm(String s ) { this.algorithm = s;}
+-
+- protected String clientAuth = "false";
+- public String getClientAuth() { return clientAuth;}
+- public void setClientAuth(String s ) { this.clientAuth = s;}
+-
+- protected String keystorePass = "changeit";
+- public String getKeystorePass() { return keystorePass;}
+- public void setKeystorePass(String s ) { this.keystorePass = s;}
+-
+- protected String keystoreType = "JKS";
+- public String getKeystoreType() { return keystoreType;}
+- public void setKeystoreType(String s ) { this.keystoreType = s;}
+-
+- protected String sslProtocol = "TLS";
+-
+- public String getSslProtocol() { return sslProtocol;}
+- public void setSslProtocol(String s) { sslProtocol = s;}
+-
+- protected String sslEnabledProtocols=null; //"TLSv1,SSLv3,SSLv2Hello"
+- protected String[] sslEnabledProtocolsarr = new String[0];
+- public void setSslEnabledProtocols(String s) {
+- this.sslEnabledProtocols = s;
+- StringTokenizer t = new StringTokenizer(s,",");
+- sslEnabledProtocolsarr = new String[t.countTokens()];
+- for (int i=0; i<sslEnabledProtocolsarr.length; i++ )
sslEnabledProtocolsarr[i] = t.nextToken();
+- }
+-
+-
+- protected String ciphers = null;
+- protected String[] ciphersarr = new String[0];
+- public String getCiphers() { return ciphers;}
+- public void setCiphers(String s) {
+- ciphers = s;
+- if ( s == null ) ciphersarr = new String[0];
+- else {
+- StringTokenizer t = new StringTokenizer(s,",");
+- ciphersarr = new String[t.countTokens()];
+- for (int i=0; i<ciphersarr.length; i++ ) ciphersarr[i] = t.nextToken();
+- }
+- }
+-
+- /**
+- * SSL engine.
+- */
+- protected boolean SSLEnabled = false;
+- public boolean isSSLEnabled() { return SSLEnabled;}
+- public void setSSLEnabled(boolean SSLEnabled) {this.SSLEnabled = SSLEnabled;}
+-
+- protected boolean secure = false;
+- public boolean getSecure() { return secure;}
+- public void setSecure(boolean b) { secure = b;}
+-
+- public void setSelectorPool(NioSelectorPool selectorPool) {
+- this.selectorPool = selectorPool;
+- }
+-
+- public void setSocketProperties(SocketProperties socketProperties) {
+- this.socketProperties = socketProperties;
+- }
+-
+- public void setUseSendfile(boolean useSendfile) {
+-
+- this.useSendfile = useSendfile;
+- }
+-
+- public void setOomParachute(int oomParachute) {
+- this.oomParachute = oomParachute;
+- }
+-
+- public void setOomParachuteData(byte[] oomParachuteData) {
+- this.oomParachuteData = oomParachuteData;
+- }
+-
+- protected SSLContext sslContext = null;
+- public SSLContext getSSLContext() { return sslContext;}
+- public void setSSLContext(SSLContext c) { sslContext = c;}
+-
+- // --------------------------------------------------------- OOM Parachute Methods
+-
+- protected void checkParachute() {
+- boolean para = reclaimParachute(false);
+- if (!para && (System.currentTimeMillis()-lastParachuteCheck)>10000)
{
+- try {
+- log.fatal(oomParachuteMsg);
+- }catch (Throwable t) {
+- System.err.println(oomParachuteMsg);
+- }
+- lastParachuteCheck = System.currentTimeMillis();
+- }
+- }
+-
+- protected boolean reclaimParachute(boolean force) {
+- if ( oomParachuteData != null ) return true;
+- if ( oomParachute > 0 && ( force ||
(Runtime.getRuntime().freeMemory() > (oomParachute*2))) )
+- oomParachuteData = new byte[oomParachute];
+- return oomParachuteData != null;
+- }
+-
+- protected void releaseCaches() {
+- this.keyCache.clear();
+- this.nioChannels.clear();
+- this.processorCache.clear();
+- if ( handler != null ) handler.releaseCaches();
+-
+- }
+-
+- // --------------------------------------------------------- Public Methods
+- /**
+- * Number of keepalive sockets.
+- */
+- public int getKeepAliveCount() {
+- if (pollers == null) {
+- return 0;
+- } else {
+- int keepAliveCount = 0;
+- for (int i = 0; i < pollers.length; i++) {
+- keepAliveCount += pollers[i].getKeepAliveCount();
+- }
+- return keepAliveCount;
+- }
+- }
+-
+-
+-
+- /**
+- * 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;
+- }
+-
+-
+- /**
+- * Return the state of the endpoint.
+- *
+- * @return true if the endpoint is running, false otherwise
+- */
+- public boolean isRunning() {
+- return running;
+- }
+-
+-
+- /**
+- * Return the state of the endpoint.
+- *
+- * @return true if the endpoint is paused, false otherwise
+- */
+- public boolean isPaused() {
+- return paused;
+- }
+-
+-
+- // ----------------------------------------------- Public Lifecycle Methods
+-
+-
+- /**
+- * Initialize the endpoint.
+- */
+- public void init()
+- throws Exception {
+-
+- if (initialized)
+- return;
+-
+- serverSock = ServerSocketChannel.open();
+-
serverSock.socket().setPerformancePreferences(socketProperties.getPerformanceConnectionTime(),
+-
socketProperties.getPerformanceLatency(),
+-
socketProperties.getPerformanceBandwidth());
+- InetSocketAddress addr = (address!=null?new InetSocketAddress(address,port):new
InetSocketAddress(port));
+- serverSock.socket().bind(addr,backlog);
+- serverSock.configureBlocking(true); //mimic APR behavior
+-
+- // Initialize thread count defaults for acceptor, poller
+- if (acceptorThreadCount == 0) {
+- // FIXME: Doesn't seem to work that well with multiple accept threads
+- acceptorThreadCount = 1;
+- }
+- if (pollerThreadCount <= 0) {
+- //minimum one poller thread
+- pollerThreadCount = 1;
+- }
+-
+- // Initialize SSL if needed
+- if (isSSLEnabled()) {
+- // Initialize SSL
+- char[] passphrase = getKeystorePass().toCharArray();
+-
+- KeyStore ks = KeyStore.getInstance(getKeystoreType());
+- ks.load(new FileInputStream(getKeystoreFile()), passphrase);
+-
+- KeyManagerFactory kmf = KeyManagerFactory.getInstance(getAlgorithm());
+- kmf.init(ks, passphrase);
+-
+- char[] tpassphrase =
(getTruststorePass()!=null)?getTruststorePass().toCharArray():passphrase;
+- String ttype =
(getTruststoreType()!=null)?getTruststoreType():getKeystoreType();
+-
+- KeyStore ts = null;
+- if (getTruststoreFile()==null) {
+- ts = KeyStore.getInstance(getKeystoreType());
+- ts.load(new FileInputStream(getKeystoreFile()), passphrase);
+- }else {
+- ts = KeyStore.getInstance(ttype);
+- ts.load(new FileInputStream(getTruststoreFile()), tpassphrase);
+- }
+-
+- TrustManagerFactory tmf = TrustManagerFactory.getInstance(getAlgorithm());
+- tmf.init(ts);
+-
+- sslContext = SSLContext.getInstance(getSslProtocol());
+- sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+-
+- }
+-
+- if (oomParachute>0) reclaimParachute(true);
+-
+- initialized = true;
+-
+- }
+-
+-
+- /**
+- * Start the NIO endpoint, creating acceptor, poller threads.
+- */
+- public void start()
+- throws Exception {
+- // Initialize socket if not done before
+- if (!initialized) {
+- init();
+- }
+- if (!running) {
+- running = true;
+- paused = false;
+-
+- // Create worker collection
+- if (getUseExecutor()) {
+- if ( executor == null ) {
+- TaskQueue taskqueue = new TaskQueue();
+- TaskThreadFactory tf = new TaskThreadFactory(getName() +
"-exec-");
+- executor = new ThreadPoolExecutor(getMinSpareThreads(),
getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
+- taskqueue.setParent( (ThreadPoolExecutor) executor);
+- }
+- } else if ( executor == null ) {//avoid two thread pools being created
+- workers = new WorkerStack(maxThreads);
+- }
+-
+- // 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();
+- }
+-
+- // Start poller threads
+- pollers = new Poller[pollerThreadCount];
+- for (int i = 0; i < pollerThreadCount; i++) {
+- pollers[i] = new Poller();
+- pollers[i].init();
+- Thread pollerThread = new Thread(pollers[i], getName() +
"-Poller-" + i);
+- pollerThread.setPriority(threadPriority);
+- pollerThread.setDaemon(true);
+- pollerThread.start();
+- }
+- }
+- }
+-
+-
+- /**
+- * 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 sockets
+- * again.
+- */
+- public void resume() {
+- if (running) {
+- paused = false;
+- }
+- }
+-
+-
+- /**
+- * Stop the endpoint. This will cause all processing threads to stop.
+- */
+- public void stop() {
+- if (running) {
+- running = false;
+- unlockAccept();
+- for (int i = 0; i < pollers.length; i++) {
+- pollers[i].destroy();
+- }
+- pollers = null;
+- }
+- eventCache.clear();
+- keyCache.clear();
+- nioChannels.clear();
+- processorCache.clear();
+- if ( executor!=null ) {
+- if ( executor instanceof ThreadPoolExecutor ) {
+- //this is our internal one, so we need to shut it down
+- ThreadPoolExecutor tpe = (ThreadPoolExecutor) executor;
+- tpe.shutdown();
+- TaskQueue queue = (TaskQueue) tpe.getQueue();
+- queue.setParent(null);
+- }
+- executor = null;
+- }
+- }
+-
+-
+- /**
+- * Deallocate NIO memory pools, and close server socket.
+- */
+- public void destroy() throws Exception {
+- if (running) {
+- stop();
+- }
+- // Close server socket
+- serverSock.socket().close();
+- serverSock.close();
+- serverSock = null;
+- sslContext = null;
+- initialized = false;
+- releaseCaches();
+- }
+-
+-
+- // ------------------------------------------------------ Protected Methods
+-
+-
+- /**
+- * Get a sequence number used for thread naming.
+- */
+- protected int getSequence() {
+- return sequence++;
+- }
+-
+- public int getWriteBufSize() {
+- return socketProperties.getTxBufSize();
+- }
+-
+- public int getReadBufSize() {
+- return socketProperties.getRxBufSize();
+- }
+-
+- public NioSelectorPool getSelectorPool() {
+- return selectorPool;
+- }
+-
+- public SocketProperties getSocketProperties() {
+- return socketProperties;
+- }
+-
+- public boolean getUseSendfile() {
+- //send file doesn't work with SSL
+- return useSendfile && (!isSSLEnabled());
+- }
+-
+- public int getOomParachute() {
+- return oomParachute;
+- }
+-
+- public byte[] getOomParachuteData() {
+- return oomParachuteData;
+- }
+-
+- /**
+- * 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("127.0.0.1", 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);
+- }
+- } 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
+- }
+- }
+- }
+- }
+-
+-
+- /**
+- * Process the specified connection.
+- */
+- protected boolean setSocketOptions(SocketChannel socket) {
+- // Process the connection
+- try {
+- //disable blocking, APR style, we are gonna be polling it
+- socket.configureBlocking(false);
+- Socket sock = socket.socket();
+- socketProperties.setProperties(sock);
+-
+- NioChannel channel = nioChannels.poll();
+- if ( channel == null ) {
+- // SSL setup
+- if (sslContext != null) {
+- SSLEngine engine = createSSLEngine();
+- int appbufsize = engine.getSession().getApplicationBufferSize();
+- NioBufferHandler bufhandler = new
NioBufferHandler(Math.max(appbufsize,socketProperties.getAppReadBufSize()),
+-
Math.max(appbufsize,socketProperties.getAppWriteBufSize()),
+-
socketProperties.getDirectBuffer());
+- channel = new SecureNioChannel(socket, engine, bufhandler,
selectorPool);
+- } else {
+- // normal tcp setup
+- NioBufferHandler bufhandler = new
NioBufferHandler(socketProperties.getAppReadBufSize(),
+-
socketProperties.getAppWriteBufSize(),
+-
socketProperties.getDirectBuffer());
+-
+- channel = new NioChannel(socket, bufhandler);
+- }
+- } else {
+- channel.setIOChannel(socket);
+- if ( channel instanceof SecureNioChannel ) {
+- SSLEngine engine = createSSLEngine();
+- ((SecureNioChannel)channel).reset(engine);
+- } else {
+- channel.reset();
+- }
+- }
+- getPoller0().register(channel);
+- } catch (Throwable t) {
+- try {
+- log.error("",t);
+- }catch ( Throwable tt){}
+- // Tell to close the socket
+- return false;
+- }
+- return true;
+- }
+-
+- protected SSLEngine createSSLEngine() {
+- SSLEngine engine = sslContext.createSSLEngine();
+- if ("false".equals(getClientAuth())) {
+- engine.setNeedClientAuth(false);
+- engine.setWantClientAuth(false);
+- } else if ("true".equals(getClientAuth()) ||
"yes".equals(getClientAuth())){
+- engine.setNeedClientAuth(true);
+- } else if ("want".equals(getClientAuth())) {
+- engine.setWantClientAuth(true);
+- }
+- engine.setUseClientMode(false);
+- if ( ciphersarr.length > 0 ) engine.setEnabledCipherSuites(ciphersarr);
+- if ( sslEnabledProtocolsarr.length > 0 )
engine.setEnabledProtocols(sslEnabledProtocolsarr);
+-
+- return engine;
+- }
+-
+-
+- /**
+- * Returns true if a worker thread is available for processing.
+- * @return boolean
+- */
+- protected boolean isWorkerAvailable() {
+- if ( executor != null ) {
+- return true;
+- } else {
+- if (workers.size() > 0) {
+- return true;
+- }
+- if ( (maxThreads > 0) && (curThreads < maxThreads)) {
+- return true;
+- } else {
+- if (maxThreads < 0) {
+- return true;
+- } else {
+- return false;
+- }
+- }
+- }
+- }
+- /**
+- * 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++;
+- 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();
+- while (workerThread == null) {
+- try {
+- synchronized (workers) {
+- workerThread = createWorkerThread();
+- if ( workerThread == null ) workers.wait();
+- }
+- } catch (InterruptedException e) {
+- // Ignore
+- }
+- if ( workerThread == null ) 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(NioChannel socket) {
+- return processSocket(socket,null);
+- }
+-
+-
+- /**
+- * Process given socket for an event.
+- */
+- protected boolean processSocket(NioChannel socket, SocketStatus status) {
+- return processSocket(socket,status,true);
+- }
+-
+- protected boolean processSocket(NioChannel socket, SocketStatus status, boolean
dispatch) {
+- try {
+- if (executor == null) {
+- getWorkerThread().assign(socket, status);
+- } else {
+- SocketProcessor sc = processorCache.poll();
+- if ( sc == null ) sc = new SocketProcessor(socket,status);
+- else sc.reset(socket,status);
+- if ( dispatch ) executor.execute(sc);
+- else sc.run();
+- }
+- } 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;
+- }
+-
+-
+- // --------------------------------------------------- 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
+- }
+- }
+- try {
+- // Accept the next incoming connection from the server socket
+- SocketChannel socket = serverSock.accept();
+- // Hand this socket off to an appropriate processor
+- //TODO FIXME - this is currently a blocking call, meaning we will be
blocking
+- //further accepts until there is a thread available.
+- if ( running && (!paused) && socket != null ) {
+- //processSocket(socket);
+- if (!setSocketOptions(socket)) {
+- try {
+- socket.socket().close();
+- socket.close();
+- } catch (IOException ix) {
+- if (log.isDebugEnabled())
+- log.debug("", ix);
+- }
+- }
+- }
+- }catch ( IOException x ) {
+- if ( running )
log.error(sm.getString("endpoint.accept.fail"), x);
+- } catch (OutOfMemoryError oom) {
+- try {
+- oomParachuteData = null;
+- releaseCaches();
+- log.error("", oom);
+- }catch ( Throwable oomt ) {
+- try {
+- try {
+- System.err.println(oomParachuteMsg);
+- oomt.printStackTrace();
+- }catch (Throwable letsHopeWeDontGetHere){}
+- }catch (Throwable letsHopeWeDontGetHere){}
+- }
+- } catch (Throwable t) {
+- log.error(sm.getString("endpoint.accept.fail"), t);
+- }
+- }//while
+- }//run
+- }
+-
+-
+- // ----------------------------------------------------- Poller Inner Classes
+-
+- /**
+- *
+- * PollerEvent, cacheable object for poller events to avoid GC
+- */
+- public class PollerEvent implements Runnable {
+-
+- protected NioChannel socket;
+- protected int interestOps;
+- protected KeyAttachment key;
+- public PollerEvent(NioChannel ch, KeyAttachment k, int intOps) {
+- reset(ch, k, intOps);
+- }
+-
+- public void reset(NioChannel ch, KeyAttachment k, int intOps) {
+- socket = ch;
+- interestOps = intOps;
+- key = k;
+- }
+-
+- public void reset() {
+- reset(null, null, 0);
+- }
+-
+- public void run() {
+- if ( interestOps == OP_REGISTER ) {
+- try {
+- socket.getIOChannel().register(socket.getPoller().getSelector(),
SelectionKey.OP_READ, key);
+- } catch (Exception x) {
+- log.error("", x);
+- }
+- } else {
+- final SelectionKey key =
socket.getIOChannel().keyFor(socket.getPoller().getSelector());
+- try {
+- boolean cancel = false;
+- if (key != null) {
+- final KeyAttachment att = (KeyAttachment) key.attachment();
+- if ( att!=null ) {
+- att.access();//to prevent timeout
+- //we are registering the key to start with, reset the
fairness counter.
+- att.setFairness(0);
+- att.interestOps(interestOps);
+- key.interestOps(interestOps);
+- } else {
+- cancel = true;
+- }
+- } else {
+- cancel = true;
+- }
+- if ( cancel )
getPoller0().cancelledKey(key,SocketStatus.ERROR,false);
+- }catch (CancelledKeyException ckx) {
+- try {
+- getPoller0().cancelledKey(key,SocketStatus.DISCONNECT,true);
+- }catch (Exception ignore) {}
+- }
+- }//end if
+- }//run
+-
+- public String toString() {
+- return
super.toString()+"[intOps="+this.interestOps+"]";
+- }
+- }
+- /**
+- * Poller class.
+- */
+- public class Poller implements Runnable {
+-
+- protected Selector selector;
+- protected ConcurrentLinkedQueue<Runnable> events = new
ConcurrentLinkedQueue<Runnable>();
+-
+- protected boolean close = false;
+- protected long nextExpiration = 0;//optimize expiration handling
+-
+- protected int keepAliveCount = 0;
+- public int getKeepAliveCount() { return keepAliveCount; }
+-
+- protected AtomicLong wakeupCounter = new AtomicLong(0l);
+-
+- protected CountDownLatch stopLatch = new CountDownLatch(1);
+-
+-
+-
+- public Poller() throws IOException {
+- this.selector = Selector.open();
+- }
+-
+- public Selector getSelector() { return selector;}
+-
+- /**
+- * Create the poller. With some versions of APR, the maximum poller size will
+- * be 62 (reocmpiling APR is necessary to remove this limitation).
+- */
+- protected void init() {
+- keepAliveCount = 0;
+- }
+-
+- /**
+- * Destroy the poller.
+- */
+- protected void destroy() {
+- // Wait for polltime before doing anything, so that the poller threads
+- // exit, otherwise parallel descturction of sockets which are still
+- // in the poller can cause problems
+- close = true;
+- events.clear();
+- selector.wakeup();
+- try { stopLatch.await(5,TimeUnit.SECONDS); } catch (InterruptedException
ignore ) {}
+- }
+-
+- public void addEvent(Runnable event) {
+- events.offer(event);
+- if ( wakeupCounter.incrementAndGet() < 3 ) selector.wakeup();
+- }
+-
+- /**
+- * 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(final NioChannel socket) {
+- add(socket,SelectionKey.OP_READ);
+- }
+-
+- public void add(final NioChannel socket, final int interestOps) {
+- PollerEvent r = eventCache.poll();
+- if ( r==null) r = new PollerEvent(socket,null,interestOps);
+- else r.reset(socket,null,interestOps);
+- addEvent(r);
+- }
+-
+- public boolean events() {
+- boolean result = false;
+- //synchronized (events) {
+- Runnable r = null;
+- result = (events.size() > 0);
+- while ( (r = (Runnable)events.poll()) != null ) {
+- try {
+- r.run();
+- if ( r instanceof PollerEvent ) {
+- ((PollerEvent)r).reset();
+- eventCache.offer((PollerEvent)r);
+- }
+- } catch ( Throwable x ) {
+- log.error("",x);
+- }
+- }
+- //events.clear();
+- //}
+- return result;
+- }
+-
+- public void register(final NioChannel socket)
+- {
+- socket.setPoller(this);
+- KeyAttachment key = keyCache.poll();
+- final KeyAttachment ka = key!=null?key:new KeyAttachment();
+- ka.reset(this,socket);
+- PollerEvent r = eventCache.poll();
+- ka.interestOps(SelectionKey.OP_READ);//this is what OP_REGISTER turns into.
+- if ( r==null) r = new PollerEvent(socket,ka,OP_REGISTER);
+- else r.reset(socket,ka,OP_REGISTER);
+- addEvent(r);
+- }
+- public void cancelledKey(SelectionKey key, SocketStatus status, boolean
dispatch) {
+- try {
+- if ( key == null ) return;//nothing to do
+- KeyAttachment ka = (KeyAttachment) key.attachment();
+- if (ka != null && ka.getComet() && status != null) {
+- //the comet event takes care of clean up
+- //processSocket(ka.getChannel(), status, dispatch);
+- ka.setComet(false);//to avoid a loop
+- processSocket(ka.getChannel(), status, false);//don't dispatch
if the lines below are cancelling the key
+- if (status == SocketStatus.TIMEOUT ) return; // don't close on
comet timeout
+- }
+- if (key.isValid()) key.cancel();
+- if (key.channel().isOpen()) try {key.channel().close();}catch (Exception
ignore){}
+- try {ka.channel.close(true);}catch (Exception ignore){}
+- key.attach(null);
+- } catch (Throwable e) {
+- if ( log.isDebugEnabled() ) log.error("",e);
+- // Ignore
+- }
+- }
+- /**
+- * 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) {
+- try {
+- // Loop if endpoint is paused
+- while (paused && (!close) ) {
+- try {
+- Thread.sleep(500);
+- } catch (InterruptedException e) {
+- // Ignore
+- }
+- }
+- boolean hasEvents = false;
+-
+- hasEvents = (hasEvents | events());
+- // Time to terminate?
+- if (close) {
+- timeout(0, false);
+- stopLatch.countDown();
+- return;
+- }
+- int keyCount = 0;
+- try {
+- if ( !close ) {
+- keyCount = selector.select(selectorTimeout);
+- wakeupCounter.set(0);
+- }
+- if (close) {
+- timeout(0, false);
+- stopLatch.countDown();
+- selector.close();
+- return;
+- }
+- } catch ( NullPointerException x ) {
+- //sun bug 5076772 on windows JDK 1.5
+- if ( wakeupCounter == null || selector == null ) throw x;
+- continue;
+- } catch ( CancelledKeyException x ) {
+- //sun bug 5076772 on windows JDK 1.5
+- if ( wakeupCounter == null || selector == null ) throw x;
+- continue;
+- } catch (Throwable x) {
+- log.error("",x);
+- continue;
+- }
+- //either we timed out or we woke up, process events first
+- if ( keyCount == 0 ) hasEvents = (hasEvents | events());
+-
+- Iterator iterator = keyCount > 0 ?
selector.selectedKeys().iterator() : null;
+- // Walk through the collection of ready keys and dispatch
+- // any active event.
+- while (iterator != null && iterator.hasNext()) {
+- SelectionKey sk = (SelectionKey) iterator.next();
+- KeyAttachment attachment = (KeyAttachment)sk.attachment();
+- iterator.remove();
+- processKey(sk, attachment);
+- }//while
+-
+- //process timeouts
+- timeout(keyCount,hasEvents);
+- if ( oomParachute > 0 && oomParachuteData == null )
checkParachute();
+- } catch (OutOfMemoryError oom) {
+- try {
+- oomParachuteData = null;
+- releaseCaches();
+- log.error("", oom);
+- }catch ( Throwable oomt ) {
+- try {
+- System.err.println(oomParachuteMsg);
+- oomt.printStackTrace();
+- }catch (Throwable letsHopeWeDontGetHere){}
+- }
+- }
+- }//while
+- synchronized (this) {
+- this.notifyAll();
+- }
+- stopLatch.countDown();
+-
+- }
+-
+- protected boolean processKey(SelectionKey sk, KeyAttachment attachment) {
+- boolean result = true;
+- try {
+- if ( close ) {
+- cancelledKey(sk, SocketStatus.STOP, false);
+- } else if ( sk.isValid() && attachment != null ) {
+- attachment.access();//make sure we don't time out valid sockets
+- sk.attach(attachment);//cant remember why this is here
+- NioChannel channel = attachment.getChannel();
+- if (sk.isReadable() || sk.isWritable() ) {
+- if ( sk.isReadable() && attachment.getReadLatch() !=
null ) {
+- unreg(sk, attachment,SelectionKey.OP_READ);
+- attachment.getReadLatch().countDown();
+- } else if ( sk.isWritable() &&
attachment.getWriteLatch() != null ) {
+- unreg(sk, attachment,SelectionKey.OP_WRITE);
+- attachment.getWriteLatch().countDown();
+- } else if ( attachment.getSendfileData() != null ) {
+- processSendfile(sk,attachment,true);
+- } else if ( attachment.getComet() ) {
+- //check if thread is available
+- if ( isWorkerAvailable() ) {
+- unreg(sk, attachment, sk.readyOps());
+- if (!processSocket(channel, SocketStatus.OPEN))
+- processSocket(channel, SocketStatus.DISCONNECT);
+- attachment.setFairness(0);
+- } else {
+- //increase the fairness counter
+- attachment.incFairness();
+- result = false;
+- }
+- } else {
+- //later on, improve latch behavior
+- if ( isWorkerAvailable() ) {
+- unreg(sk, attachment,sk.readyOps());
+- boolean close = (!processSocket(channel));
+- if (close) {
+- cancelledKey(sk,SocketStatus.DISCONNECT,false);
+- }
+- attachment.setFairness(0);
+- } else {
+- //increase the fairness counter
+- attachment.incFairness();
+- result = false;
+- }
+- }
+- }
+- } else {
+- //invalid key
+- cancelledKey(sk, SocketStatus.ERROR,false);
+- }
+- } catch ( CancelledKeyException ckx ) {
+- cancelledKey(sk, SocketStatus.ERROR,false);
+- } catch (Throwable t) {
+- log.error("",t);
+- }
+- return result;
+- }
+-
+- public boolean processSendfile(SelectionKey sk, KeyAttachment attachment,
boolean reg) {
+- try {
+- //unreg(sk,attachment);//only do this if we do process send file on a
separate thread
+- SendfileData sd = attachment.getSendfileData();
+- if ( sd.fchannel == null ) {
+- File f = new File(sd.fileName);
+- if ( !f.exists() ) {
+- cancelledKey(sk,SocketStatus.ERROR,false);
+- return false;
+- }
+- sd.fchannel = new FileInputStream(f).getChannel();
+- }
+- SocketChannel sc = attachment.getChannel().getIOChannel();
+- long written = sd.fchannel.transferTo(sd.pos,sd.length,sc);
+- if ( written > 0 ) {
+- sd.pos += written;
+- sd.length -= written;
+- }
+- if ( sd.length <= 0 ) {
+- attachment.setSendfileData(null);
+- if ( sd.keepAlive )
+- if (reg) reg(sk,attachment,SelectionKey.OP_READ);
+- else
+- cancelledKey(sk,SocketStatus.STOP,false);
+- } else if ( attachment.interestOps() == 0 && reg ) {
+- reg(sk,attachment,SelectionKey.OP_WRITE);
+- }
+- }catch ( IOException x ) {
+- if ( log.isDebugEnabled() ) log.warn("Unable to complete sendfile
request:", x);
+- cancelledKey(sk,SocketStatus.ERROR,false);
+- return false;
+- }catch ( Throwable t ) {
+- log.error("",t);
+- cancelledKey(sk, SocketStatus.ERROR, false);
+- return false;
+- }
+- return true;
+- }
+-
+- protected void unreg(SelectionKey sk, KeyAttachment attachment, int readyOps) {
+- //this is a must, so that we don't have multiple threads messing with
the socket
+- reg(sk,attachment,sk.interestOps()& (~readyOps));
+- }
+-
+- protected void reg(SelectionKey sk, KeyAttachment attachment, int intops) {
+- sk.interestOps(intops);
+- attachment.interestOps(intops);
+- }
+-
+- protected void timeout(int keyCount, boolean hasEvents) {
+- long now = System.currentTimeMillis();
+- //don't process timeouts too frequently, but if the selector simply
timed out
+- //then we can check timeouts to avoid gaps
+- if ( ((keyCount>0 || hasEvents) ||(now < nextExpiration)) &&
(!close) ) {
+- return;
+- }
+- long prevExp = nextExpiration;
+- nextExpiration = now + socketProperties.getTimeoutInterval();
+- //timeout
+- Set<SelectionKey> keys = selector.keys();
+- int keycount = 0;
+- for (Iterator<SelectionKey> iter = keys.iterator(); iter.hasNext(); )
{
+- SelectionKey key = iter.next();
+- keycount++;
+- try {
+- KeyAttachment ka = (KeyAttachment) key.attachment();
+- if ( ka == null ) {
+- cancelledKey(key, SocketStatus.ERROR,false); //we don't
support any keys without attachments
+- } else if ( ka.getError() ) {
+- cancelledKey(key, SocketStatus.ERROR,true);
+- }else if ((ka.interestOps()&SelectionKey.OP_READ) ==
SelectionKey.OP_READ) {
+- //only timeout sockets that we are waiting for a read from
+- long delta = now - ka.getLastAccess();
+- long timeout = (ka.getTimeout()==-1)?((long)
socketProperties.getSoTimeout()):(ka.getTimeout());
+- boolean isTimedout = delta > timeout;
+- if ( close ) {
+- key.interestOps(0);
+- ka.interestOps(0); //avoid duplicate stop calls
+- processKey(key,ka);
+- } else if (isTimedout) {
+- key.interestOps(0);
+- ka.interestOps(0); //avoid duplicate timeout calls
+- cancelledKey(key, SocketStatus.TIMEOUT,true);
+- } else {
+- long nextTime = now+(timeout-delta);
+- nextExpiration = (nextTime <
nextExpiration)?nextTime:nextExpiration;
+- }
+- }//end if
+- }catch ( CancelledKeyException ckx ) {
+- cancelledKey(key, SocketStatus.ERROR,false);
+- }
+- }//for
+- if ( log.isDebugEnabled() ) log.debug("timeout completed:
keycount="+keycount+"; now="+now+";
nextExpiration="+prevExp+"; "+
+- "keyCount="+keyCount+";
hasEvents="+hasEvents +"; eval="+( (now < prevExp) &&
(keyCount>0 || hasEvents) && (!close) ));
+-
+- }
+- }
+-
+-// ----------------------------------------------------- Key Attachment Class
+- public static class KeyAttachment {
+-
+- public KeyAttachment() {
+-
+- }
+- public void reset(Poller poller, NioChannel channel) {
+- this.channel = channel;
+- this.poller = poller;
+- lastAccess = System.currentTimeMillis();
+- currentAccess = false;
+- comet = false;
+- timeout = -1;
+- error = false;
+- fairness = 0;
+- lastRegistered = 0;
+- sendfileData = null;
+- if ( readLatch!=null ) try {for (int i=0;
i<(int)readLatch.getCount();i++) readLatch.countDown();}catch (Exception ignore){}
+- readLatch = null;
+- if ( writeLatch!=null ) try {for (int i=0;
i<(int)writeLatch.getCount();i++) writeLatch.countDown();}catch (Exception ignore){}
+- writeLatch = null;
+- }
+-
+- public void reset() {
+- reset(null,null);
+- }
+-
+- public Poller getPoller() { return poller;}
+- public void setPoller(Poller poller){this.poller = poller;}
+- public long getLastAccess() { return lastAccess; }
+- public void access() { access(System.currentTimeMillis()); }
+- public void access(long access) { lastAccess = access; }
+- public void setComet(boolean comet) { this.comet = comet; }
+- public boolean getComet() { return comet; }
+- public boolean getCurrentAccess() { return currentAccess; }
+- public void setCurrentAccess(boolean access) { currentAccess = access; }
+- public Object getMutex() {return mutex;}
+- public void setTimeout(long timeout) {this.timeout = timeout;}
+- public long getTimeout() {return this.timeout;}
+- public boolean getError() { return error; }
+- public void setError(boolean error) { this.error = error; }
+- public NioChannel getChannel() { return channel;}
+- public void setChannel(NioChannel channel) { this.channel = channel;}
+- protected Poller poller = null;
+- protected int interestOps = 0;
+- public int interestOps() { return interestOps;}
+- public int interestOps(int ops) { this.interestOps = ops; return ops; }
+- public CountDownLatch getReadLatch() { return readLatch; }
+- public CountDownLatch getWriteLatch() { return writeLatch; }
+- protected CountDownLatch resetLatch(CountDownLatch latch) {
+- if ( latch.getCount() == 0 ) return null;
+- else throw new IllegalStateException("Latch must be at count 0");
+- }
+- public void resetReadLatch() { readLatch = resetLatch(readLatch); }
+- public void resetWriteLatch() { writeLatch = resetLatch(writeLatch); }
+-
+- protected CountDownLatch startLatch(CountDownLatch latch, int cnt) {
+- if ( latch == null || latch.getCount() == 0 ) {
+- return new CountDownLatch(cnt);
+- }
+- else throw new IllegalStateException("Latch must be at count 0 or
null.");
+- }
+- public void startReadLatch(int cnt) { readLatch = startLatch(readLatch,cnt);}
+- public void startWriteLatch(int cnt) { writeLatch =
startLatch(writeLatch,cnt);}
+-
+-
+- protected void awaitLatch(CountDownLatch latch, long timeout, TimeUnit unit)
throws InterruptedException {
+- if ( latch == null ) throw new IllegalStateException("Latch cannot be
null");
+- latch.await(timeout,unit);
+- }
+- public void awaitReadLatch(long timeout, TimeUnit unit) throws
InterruptedException { awaitLatch(readLatch,timeout,unit);}
+- public void awaitWriteLatch(long timeout, TimeUnit unit) throws
InterruptedException { awaitLatch(writeLatch,timeout,unit);}
+-
+- public int getFairness() { return fairness; }
+- public void setFairness(int f) { fairness = f;}
+- public void incFairness() { fairness++; }
+- public long getLastRegistered() { return lastRegistered; };
+- public void setLastRegistered(long reg) { lastRegistered = reg; }
+-
+- public void setSendfileData(SendfileData sf) { this.sendfileData = sf;}
+- public SendfileData getSendfileData() { return this.sendfileData;}
+-
+- protected Object mutex = new Object();
+- protected long lastAccess = -1;
+- protected boolean currentAccess = false;
+- protected boolean comet = false;
+- protected long timeout = -1;
+- protected boolean error = false;
+- protected NioChannel channel = null;
+- protected CountDownLatch readLatch = null;
+- protected CountDownLatch writeLatch = null;
+- protected int fairness = 0;
+- protected long lastRegistered = 0;
+- protected SendfileData sendfileData = null;
+- }
+- // ----------------------------------------------------- Worker Inner Class
+-
+-
+- /**
+- * Server processor class.
+- */
+- protected class Worker implements Runnable {
+-
+-
+- protected Thread thread = null;
+- protected boolean available = false;
+- protected Object 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(Object 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;
+- status = null;
+- available = true;
+- notifyAll();
+-
+- }
+-
+-
+- protected synchronized void assign(Object 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.
+- */
+- protected synchronized Object 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
+- Object 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) {
+- NioChannel socket = null;
+- SelectionKey key = null;
+- try {
+- // Wait for the next socket to be assigned
+- Object channel = await();
+- if (channel == null)
+- continue;
+-
+- if ( channel instanceof SocketChannel) {
+- SocketChannel sc = (SocketChannel)channel;
+- if ( !setSocketOptions(sc) ) {
+- try {
+- sc.socket().close();
+- sc.close();
+- }catch ( IOException ix ) {
+- if ( log.isDebugEnabled() ) log.debug("",ix);
+- }
+- } else {
+- //now we have it registered, remove it from the cache
+-
+- }
+- } else {
+- socket = (NioChannel)channel;
+- SocketProcessor sc = processorCache.poll();
+- if ( sc == null ) sc = new SocketProcessor(socket,status);
+- else sc.reset(socket,status);
+- sc.run();
+- }
+- }catch(CancelledKeyException cx) {
+- if (socket!=null && key!=null)
socket.getPoller().cancelledKey(key,null,false);
+- } catch (OutOfMemoryError oom) {
+- try {
+- oomParachuteData = null;
+- releaseCaches();
+- log.error("", oom);
+- }catch ( Throwable oomt ) {
+- try {
+- System.err.println(oomParachuteMsg);
+- oomt.printStackTrace();
+- }catch (Throwable letsHopeWeDontGetHere){}
+- }
+- } finally {
+- //dereference socket to let GC do its job
+- socket = null;
+- // 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.setPriority(getThreadPriority());
+- thread.start();
+- }
+-
+-
+- }
+-
+- // ------------------------------------------------ Application Buffer Handler
+- public class NioBufferHandler implements ApplicationBufferHandler {
+- protected ByteBuffer readbuf = null;
+- protected ByteBuffer writebuf = null;
+-
+- public NioBufferHandler(int readsize, int writesize, boolean direct) {
+- if ( direct ) {
+- readbuf = ByteBuffer.allocateDirect(readsize);
+- writebuf = ByteBuffer.allocateDirect(writesize);
+- }else {
+- readbuf = ByteBuffer.allocate(readsize);
+- writebuf = ByteBuffer.allocate(writesize);
+- }
+- }
+-
+- public ByteBuffer expand(ByteBuffer buffer, int remaining) {return buffer;}
+- public ByteBuffer getReadBuffer() {return readbuf;}
+- public ByteBuffer getWriteBuffer() {return writebuf;}
+-
+- }
+-
+- // ------------------------------------------------ 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(NioChannel socket);
+- public SocketState event(NioChannel socket, SocketStatus status);
+- public void releaseCaches();
+- }
+-
+-
+- // ------------------------------------------------- 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);
+- }
+- }
+-
+-
+- // ---------------------------------------------- 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 NioChannel socket = null;
+- protected SocketStatus status = null;
+-
+- public SocketProcessor(NioChannel socket, SocketStatus status) {
+- reset(socket,status);
+- }
+-
+- public void reset(NioChannel socket, SocketStatus status) {
+- this.socket = socket;
+- this.status = status;
+- }
+-
+- public void run() {
+- SelectionKey key = null;
+- try {
+- key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());
+- int handshake = -1;
+-
+- try {
+- if (key!=null) handshake = socket.handshake(key.isReadable(),
key.isWritable());
+- }catch ( IOException x ) {
+- handshake = -1;
+- if ( log.isDebugEnabled() ) log.debug("Error during SSL
handshake",x);
+- }catch ( CancelledKeyException ckx ) {
+- handshake = -1;
+- }
+- if ( handshake == 0 ) {
+- // Process the request from this socket
+- boolean closed =
(status==null)?(handler.process(socket)==Handler.SocketState.CLOSED) :
+- (handler.event(socket,status)==Handler.SocketState.CLOSED);
+-
+- if (closed) {
+- // Close socket and pool
+- try {
+- KeyAttachment ka = null;
+- if (key!=null) {
+- ka = (KeyAttachment) key.attachment();
+- if (ka!=null) ka.setComet(false);
+- socket.getPoller().cancelledKey(key, SocketStatus.ERROR,
false);
+- }
+- if (socket!=null) nioChannels.offer(socket);
+- socket = null;
+- if ( ka!=null ) keyCache.offer(ka);
+- ka = null;
+- }catch ( Exception x ) {
+- log.error("",x);
+- }
+- }
+- } else if (handshake == -1 ) {
+- KeyAttachment ka = null;
+- if (key!=null) {
+- ka = (KeyAttachment) key.attachment();
+- socket.getPoller().cancelledKey(key, SocketStatus.DISCONNECT,
false);
+- }
+- if (socket!=null) nioChannels.offer(socket);
+- socket = null;
+- if ( ka!=null ) keyCache.offer(ka);
+- ka = null;
+- } else {
+- final SelectionKey fk = key;
+- final int intops = handshake;
+- final KeyAttachment ka = (KeyAttachment)fk.attachment();
+- ka.getPoller().add(socket,intops);
+- }
+- }catch(CancelledKeyException cx) {
+- socket.getPoller().cancelledKey(key,null,false);
+- } catch (OutOfMemoryError oom) {
+- try {
+- oomParachuteData = null;
+- socket.getPoller().cancelledKey(key,SocketStatus.ERROR,false);
+- releaseCaches();
+- log.error("", oom);
+- }catch ( Throwable oomt ) {
+- try {
+- System.err.println(oomParachuteMsg);
+- oomt.printStackTrace();
+- }catch (Throwable letsHopeWeDontGetHere){}
+- }
+- }catch ( Throwable t ) {
+- log.error("",t);
+- socket.getPoller().cancelledKey(key,SocketStatus.ERROR,false);
+- } finally {
+- socket = null;
+- status = null;
+- //return to cache
+- processorCache.offer(this);
+- }
+- }
+-
+- }
+-
+- // ---------------------------------------------- TaskQueue Inner Class
+- public static class TaskQueue extends LinkedBlockingQueue<Runnable> {
+- ThreadPoolExecutor parent = null;
+-
+- public TaskQueue() {
+- super();
+- }
+-
+- public TaskQueue(int initialCapacity) {
+- super(initialCapacity);
+- }
+-
+- public TaskQueue(Collection<? extends Runnable> c) {
+- super(c);
+- }
+-
+-
+- public void setParent(ThreadPoolExecutor tp) {
+- parent = tp;
+- }
+-
+- public boolean offer(Runnable o) {
+- //we can't do any checks
+- if (parent==null) return super.offer(o);
+- //we are maxed out on threads, simply queue the object
+- if (parent.getPoolSize() == parent.getMaximumPoolSize()) return
super.offer(o);
+- //we have idle threads, just add it to the queue
+- //this is an approximation, so it could use some tuning
+- if (parent.getActiveCount()<(parent.getPoolSize())) return
super.offer(o);
+- //if we have less threads than maximum force creation of a new thread
+- if (parent.getPoolSize()<parent.getMaximumPoolSize()) return false;
+- //if we reached here, we need to add it to the queue
+- return super.offer(o);
+- }
+- }
+-
+- // ---------------------------------------------- ThreadFactory Inner Class
+- class TaskThreadFactory implements ThreadFactory {
+- final ThreadGroup group;
+- final AtomicInteger threadNumber = new AtomicInteger(1);
+- final String namePrefix;
+-
+- TaskThreadFactory(String namePrefix) {
+- SecurityManager s = System.getSecurityManager();
+- group = (s != null)? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
+- this.namePrefix = namePrefix;
+- }
+-
+- public Thread newThread(Runnable r) {
+- Thread t = new Thread(group, r, namePrefix +
threadNumber.getAndIncrement());
+- t.setDaemon(daemon);
+- t.setPriority(getThreadPriority());
+- return t;
+- }
+- }
+-
+- // ----------------------------------------------- SendfileData Inner Class
+-
+-
+- /**
+- * SendfileData class.
+- */
+- public static class SendfileData {
+- // File
+- public String fileName;
+- public FileChannel fchannel;
+- public long pos;
+- public long length;
+- // KeepAlive flag
+- public boolean keepAlive;
+- }
+-
+-}
+Index: java/org/apache/jk/common/ChannelNioSocket.java
+===================================================================
+--- java/org/apache/jk/common/ChannelNioSocket.java (revision 590752)
++++ java/org/apache/jk/common/ChannelNioSocket.java (working copy)
+@@ -1,1199 +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.jk.common;
+-
+-import java.util.Set;
+-import java.util.Iterator;
+-import java.io.IOException;
+-import java.io.InputStream;
+-import java.io.OutputStream;
+-import java.nio.ByteBuffer;
+-import java.nio.channels.Selector;
+-import java.nio.channels.SelectionKey;
+-import java.nio.channels.SocketChannel;
+-import java.nio.channels.ClosedSelectorException;
+-import java.nio.channels.ServerSocketChannel;
+-import java.nio.channels.CancelledKeyException;
+-import java.nio.channels.ClosedChannelException;
+-import java.net.URLEncoder;
+-import java.net.InetAddress;
+-import java.net.InetSocketAddress;
+-import java.net.ServerSocket;
+-import java.net.Socket;
+-import java.net.SocketException;
+-import java.net.SocketTimeoutException;
+-
+-import javax.management.ListenerNotFoundException;
+-import javax.management.MBeanNotificationInfo;
+-import javax.management.Notification;
+-import javax.management.NotificationBroadcaster;
+-import javax.management.NotificationBroadcasterSupport;
+-import javax.management.NotificationFilter;
+-import javax.management.NotificationListener;
+-import javax.management.ObjectName;
+-
+-import org.apache.tomcat.util.modeler.Registry;
+-import org.apache.jk.core.JkHandler;
+-import org.apache.jk.core.Msg;
+-import org.apache.jk.core.MsgContext;
+-import org.apache.jk.core.JkChannel;
+-import org.apache.jk.core.WorkerEnv;
+-import org.apache.coyote.Request;
+-import org.apache.coyote.RequestGroupInfo;
+-import org.apache.coyote.RequestInfo;
+-import org.apache.tomcat.util.threads.ThreadPool;
+-import org.apache.tomcat.util.threads.ThreadPoolRunnable;
+-
+-/**
+- * Accept ( and send ) TCP messages.
+- *
+- * @author Costin Manolache
+- * @author Bill Barker
+- * jmx:mbean name="jk:service=ChannelNioSocket"
+- * description="Accept socket connections"
+- * jmx:notification name="org.apache.coyote.INVOKE
+- * jmx:notification-handler name="org.apache.jk.JK_SEND_PACKET
+- * jmx:notification-handler name="org.apache.jk.JK_RECEIVE_PACKET
+- * jmx:notification-handler name="org.apache.jk.JK_FLUSH
+- *
+- * Jk can use multiple protocols/transports.
+- * Various container adapters should load this object ( as a bean ),
+- * set configurations and use it. Note that the connector will handle
+- * all incoming protocols - it's not specific to ajp1x. The protocol
+- * is abstracted by MsgContext/Message/Channel.
+- *
+- * A lot of the 'original' behavior is hardcoded - this uses Ajp13 wire
protocol,
+- * TCP, Ajp14 API etc.
+- * As we add other protocols/transports/APIs this will change, the current goal
+- * is to get the same level of functionality as in the original jk connector.
+- *
+- * XXX Make the 'message type' pluggable
+- */
+-public class ChannelNioSocket extends JkHandler
+- implements NotificationBroadcaster, JkChannel {
+- private static org.apache.juli.logging.Log log =
+- org.apache.juli.logging.LogFactory.getLog( ChannelNioSocket.class );
+-
+- private int startPort=8009;
+- private int maxPort=8019; // 0 for backward compat.
+- private int port=startPort;
+- private InetAddress inet;
+- private int serverTimeout = 0;
+- private boolean tcpNoDelay=true; // nodelay to true by default
+- private int linger=100;
+- private int socketTimeout = 0;
+- private boolean nioIsBroken = false;
+- private Selector selector = null;
+- private int bufferSize = 8*1024;
+- private int packetSize = 8*1024;
+-
+- private long requestCount=0;
+-
+- /* Turning this to true will reduce the latency with about 20%.
+- But it requires changes in tomcat to make sure client-requested
+- flush() is honored ( on my test, I got 367->433 RPS and
+- 52->35ms average time with a simple servlet )
+- */
+-
+- ThreadPool tp=ThreadPool.createThreadPool(true);
+-
+- /* ==================== Tcp socket options ==================== */
+-
+- /**
+- * jmx:managed-constructor description="default constructor"
+- */
+- public ChannelNioSocket() {
+- // This should be integrated with the domain setup
+- }
+-
+- public ThreadPool getThreadPool() {
+- return tp;
+- }
+-
+- public long getRequestCount() {
+- return requestCount;
+- }
+-
+- /** Set the port for the ajp13 channel.
+- * To support seemless load balancing and jni, we treat this
+- * as the 'base' port - we'll try up until we find one that is not
+- * used. We'll also provide the 'difference' to the main coyote
+- * handler - that will be our 'sessionID' and the position in
+- * the scoreboard and the suffix for the unix domain socket.
+- *
+- * jmx:managed-attribute description="Port to listen"
access="READ_WRITE"
+- */
+- public void setPort( int port ) {
+- this.startPort=port;
+- this.port=port;
+- this.maxPort=port+10;
+- }
+-
+- public int getPort() {
+- return port;
+- }
+-
+- public void setAddress(InetAddress inet) {
+- this.inet=inet;
+- }
+-
+- public void setBufferSize(int bs) {
+- if(bs > 8*1024) {
+- bufferSize = bs;
+- }
+- }
+-
+- public int getBufferSize() {
+- return bufferSize;
+- }
+-
+- public void setPacketSize(int ps) {
+- if(ps < 8*1024) {
+- ps = 8*1024;
+- }
+- packetSize = ps;
+- }
+-
+- public int getPacketSize() {
+- return packetSize;
+- }
+-
+- /**
+- * jmx:managed-attribute description="Bind on a specified address"
access="READ_WRITE"
+- */
+- public void setAddress(String inet) {
+- try {
+- this.inet= InetAddress.getByName( inet );
+- } catch( Exception ex ) {
+- log.error("Error parsing "+inet,ex);
+- }
+- }
+-
+- public String getAddress() {
+- if( inet!=null)
+- return inet.toString();
+- return "/0.0.0.0";
+- }
+-
+- /**
+- * Sets the timeout in ms of the server sockets created by this
+- * server. This method allows the developer to make servers
+- * more or less responsive to having their server sockets
+- * shut down.
+- *
+- * <p>By default this value is 1000ms.
+- */
+- public void setServerTimeout(int timeout) {
+- this.serverTimeout = timeout;
+- }
+- public int getServerTimeout() {
+- return serverTimeout;
+- }
+-
+- public void setTcpNoDelay( boolean b ) {
+- tcpNoDelay=b;
+- }
+-
+- public boolean getTcpNoDelay() {
+- return tcpNoDelay;
+- }
+-
+- public void setSoLinger( int i ) {
+- linger=i;
+- }
+-
+- public int getSoLinger() {
+- return linger;
+- }
+-
+- public void setSoTimeout( int i ) {
+- socketTimeout=i;
+- }
+-
+- public int getSoTimeout() {
+- return socketTimeout;
+- }
+-
+- public void setMaxPort( int i ) {
+- maxPort=i;
+- }
+-
+- public int getMaxPort() {
+- return maxPort;
+- }
+-
+- /** At startup we'll look for the first free port in the range.
+- The difference between this port and the beggining of the range
+- is the 'id'.
+- This is usefull for lb cases ( less config ).
+- */
+- public int getInstanceId() {
+- return port-startPort;
+- }
+-
+- /** If set to false, the thread pool will be created in
+- * non-daemon mode, and will prevent main from exiting
+- */
+- public void setDaemon( boolean b ) {
+- tp.setDaemon( b );
+- }
+-
+- public boolean getDaemon() {
+- return tp.getDaemon();
+- }
+-
+-
+- public void setMaxThreads( int i ) {
+- if( log.isDebugEnabled()) log.debug("Setting maxThreads " + i);
+- tp.setMaxThreads(i);
+- }
+-
+- public void setMinSpareThreads( int i ) {
+- if( log.isDebugEnabled()) log.debug("Setting minSpareThreads " + i);
+- tp.setMinSpareThreads(i);
+- }
+-
+- public void setMaxSpareThreads( int i ) {
+- if( log.isDebugEnabled()) log.debug("Setting maxSpareThreads " + i);
+- tp.setMaxSpareThreads(i);
+- }
+-
+- public int getMaxThreads() {
+- return tp.getMaxThreads();
+- }
+-
+- public int getMinSpareThreads() {
+- return tp.getMinSpareThreads();
+- }
+-
+- public int getMaxSpareThreads() {
+- return tp.getMaxSpareThreads();
+- }
+-
+- public void setBacklog(int i) {
+- }
+-
+- public void setNioIsBroken(boolean nib) {
+- nioIsBroken = nib;
+- }
+-
+- public boolean getNioIsBroken() {
+- return nioIsBroken;
+- }
+-
+- /* ==================== ==================== */
+- ServerSocket sSocket;
+- final int socketNote=1;
+- final int isNote=2;
+- final int osNote=3;
+- final int notifNote=4;
+- boolean paused = false;
+-
+- public void pause() throws Exception {
+- synchronized(this) {
+- paused = true;
+- }
+- }
+-
+- public void resume() {
+- synchronized(this) {
+- paused = false;
+- notify();
+- }
+- }
+-
+-
+- public void accept( MsgContext ep ) throws IOException {
+- if( sSocket==null ) return;
+- synchronized(this) {
+- while(paused) {
+- try{
+- wait();
+- } catch(InterruptedException ie) {
+- //Ignore, since can't happen
+- }
+- }
+- }
+- SocketChannel sc=sSocket.getChannel().accept();
+- Socket s = sc.socket();
+- ep.setNote( socketNote, s );
+- if(log.isDebugEnabled() )
+- log.debug("Accepted socket " + s +" channel " +
sc.isBlocking());
+-
+- try {
+- setSocketOptions(s);
+- } catch(SocketException sex) {
+- log.debug("Error initializing Socket Options", sex);
+- }
+-
+- requestCount++;
+-
+- sc.configureBlocking(false);
+- InputStream is=new SocketInputStream(sc);
+- OutputStream os = new SocketOutputStream(sc);
+- ep.setNote( isNote, is );
+- ep.setNote( osNote, os );
+- ep.setControl( tp );
+- }
+-
+- private void setSocketOptions(Socket s) throws SocketException {
+- if( socketTimeout > 0 )
+- s.setSoTimeout( socketTimeout );
+-
+- s.setTcpNoDelay( tcpNoDelay ); // set socket tcpnodelay state
+-
+- if( linger > 0 )
+- s.setSoLinger( true, linger);
+- }
+-
+- public void resetCounters() {
+- requestCount=0;
+- }
+-
+- /** Called after you change some fields at runtime using jmx.
+- Experimental for now.
+- */
+- public void reinit() throws IOException {
+- destroy();
+- init();
+- }
+-
+- /**
+- * jmx:managed-operation
+- */
+- public void init() throws IOException {
+- // Find a port.
+- if (startPort == 0) {
+- port = 0;
+- if(log.isInfoEnabled())
+- log.info("JK: ajp13 disabling channelNioSocket");
+- running = true;
+- return;
+- }
+- if (maxPort < startPort)
+- maxPort = startPort;
+- ServerSocketChannel ssc = ServerSocketChannel.open();
+- ssc.configureBlocking(false);
+- for( int i=startPort; i<=maxPort; i++ ) {
+- try {
+- InetSocketAddress iddr = null;
+- if( inet == null ) {
+- iddr = new InetSocketAddress( i);
+- } else {
+- iddr=new InetSocketAddress( inet, i);
+- }
+- sSocket = ssc.socket();
+- sSocket.bind(iddr);
+- port=i;
+- break;
+- } catch( IOException ex ) {
+- if(log.isInfoEnabled())
+- log.info("Port busy " + i + " " +
ex.toString());
+- sSocket = null;
+- }
+- }
+-
+- if( sSocket==null ) {
+- log.error("Can't find free port " + startPort + " "
+ maxPort );
+- return;
+- }
+- if(log.isInfoEnabled())
+- log.info("JK: ajp13 listening on " + getAddress() + ":"
+ port );
+-
+- selector = Selector.open();
+- ssc.register(selector, SelectionKey.OP_ACCEPT);
+- // If this is not the base port and we are the 'main' channleSocket and
+- // SHM didn't already set the localId - we'll set the instance id
+- if( "channelNioSocket".equals( name ) &&
+- port != startPort &&
+- (wEnv.getLocalId()==0) ) {
+- wEnv.setLocalId( port - startPort );
+- }
+-
+- // XXX Reverse it -> this is a notification generator !!
+- if( next==null && wEnv!=null ) {
+- if( nextName!=null )
+- setNext( wEnv.getHandler( nextName ) );
+- if( next==null )
+- next=wEnv.getHandler( "dispatch" );
+- if( next==null )
+- next=wEnv.getHandler( "request" );
+- }
+- JMXRequestNote =wEnv.getNoteId( WorkerEnv.ENDPOINT_NOTE,
"requestNote");
+- running = true;
+-
+- // Run a thread that will accept connections.
+- // XXX Try to find a thread first - not sure how...
+- if( this.domain != null ) {
+- try {
+- tpOName=new ObjectName(domain + ":type=ThreadPool,name=" +
+- getChannelName());
+-
+- Registry.getRegistry(null, null)
+- .registerComponent(tp, tpOName, null);
+-
+- rgOName = new ObjectName
+- (domain+":type=GlobalRequestProcessor,name=" +
getChannelName());
+- Registry.getRegistry(null, null)
+- .registerComponent(global, rgOName, null);
+- } catch (Exception e) {
+- log.error("Can't register threadpool" );
+- }
+- }
+-
+- tp.start();
+- Poller pollAjp = new Poller();
+- tp.runIt(pollAjp);
+- }
+-
+- ObjectName tpOName;
+- ObjectName rgOName;
+- RequestGroupInfo global=new RequestGroupInfo();
+- int JMXRequestNote;
+-
+- public void start() throws IOException{
+- if( sSocket==null )
+- init();
+- resume();
+- }
+-
+- public void stop() throws IOException {
+- destroy();
+- }
+-
+- public void registerRequest(Request req, MsgContext ep, int count) {
+- if(this.domain != null) {
+- try {
+- RequestInfo rp=req.getRequestProcessor();
+- rp.setGlobalProcessor(global);
+- ObjectName roname = new ObjectName
+- (getDomain() + ":type=RequestProcessor,worker="+
+- getChannelName()+",name=JkRequest" +count);
+- ep.setNote(JMXRequestNote, roname);
+-
+- Registry.getRegistry(null, null).registerComponent( rp, roname, null);
+- } catch( Exception ex ) {
+- log.warn("Error registering request");
+- }
+- }
+- }
+-
+- public void open(MsgContext ep) throws IOException {
+- }
+-
+-
+- public void close(MsgContext ep) throws IOException {
+- Socket s=(Socket)ep.getNote( socketNote );
+- SelectionKey key = s.getChannel().keyFor(selector);
+- if(key != null) {
+- key.cancel();
+- }
+- s.close();
+- }
+-
+- public void destroy() throws IOException {
+- running = false;
+- try {
+- /* If we disabled the channel return */
+- if (port == 0)
+- return;
+- tp.shutdown();
+-
+- selector.wakeup().close();
+- sSocket.close(); // XXX?
+-
+- if( tpOName != null ) {
+- Registry.getRegistry(null, null).unregisterComponent(tpOName);
+- }
+- if( rgOName != null ) {
+- Registry.getRegistry(null, null).unregisterComponent(rgOName);
+- }
+- } catch(Exception e) {
+- log.info("Error shutting down the channel " + port + " "
+
+- e.toString());
+- if( log.isDebugEnabled() ) log.debug("Trace", e);
+- }
+- }
+-
+- public int send( Msg msg, MsgContext ep)
+- throws IOException {
+- msg.end(); // Write the packet header
+- byte buf[]=msg.getBuffer();
+- int len=msg.getLen();
+-
+- if(log.isTraceEnabled() )
+- log.trace("send() " + len + " " + buf[4] );
+-
+- OutputStream os=(OutputStream)ep.getNote( osNote );
+- os.write( buf, 0, len );
+- return len;
+- }
+-
+- public int flush( Msg msg, MsgContext ep)
+- throws IOException {
+- OutputStream os=(OutputStream)ep.getNote( osNote );
+- os.flush();
+- return 0;
+- }
+-
+- public int receive( Msg msg, MsgContext ep )
+- throws IOException {
+- if (log.isTraceEnabled()) {
+- log.trace("receive() ");
+- }
+-
+- byte buf[]=msg.getBuffer();
+- int hlen=msg.getHeaderLength();
+-
+- // XXX If the length in the packet header doesn't agree with the
+- // actual number of bytes read, it should probably return an error
+- // value. Also, callers of this method never use the length
+- // returned -- should probably return true/false instead.
+-
+- int rd = this.read(ep, buf, 0, hlen );
+-
+- if(rd < 0) {
+- // Most likely normal apache restart.
+- // log.warn("Wrong message " + rd );
+- return rd;
+- }
+-
+- msg.processHeader();
+-
+- /* After processing the header we know the body
+- length
+- */
+- int blen=msg.getLen();
+-
+- // XXX check if enough space - it's assert()-ed !!!
+-
+- int total_read = 0;
+-
+- total_read = this.read(ep, buf, hlen, blen);
+-
+- if ((total_read <= 0) && (blen > 0)) {
+- log.warn("can't read body, waited #" + blen);
+- return -1;
+- }
+-
+- if (total_read != blen) {
+- log.warn( "incomplete read, waited #" + blen +
+- " got only " + total_read);
+- return -2;
+- }
+-
+- return total_read;
+- }
+-
+- /**
+- * Read N bytes from the InputStream, and ensure we got them all
+- * Under heavy load we could experience many fragmented packets
+- * just read Unix Network Programming to recall that a call to
+- * read didn't ensure you got all the data you want
+- *
+- * from read() Linux manual
+- *
+- * On success, the number of bytes read is returned (zero indicates end
+- * of file),and the file position is advanced by this number.
+- * It is not an error if this number is smaller than the number of bytes
+- * requested; this may happen for example because fewer bytes
+- * are actually available right now (maybe because we were close to
+- * end-of-file, or because we are reading from a pipe, or from a
+- * terminal), or because read() was interrupted by a signal.
+- * On error, -1 is returned, and errno is set appropriately. In this
+- * case it is left unspecified whether the file position (if any) changes.
+- *
+- **/
+- public int read( MsgContext ep, byte[] b, int offset, int len)
+- throws IOException
+- {
+- InputStream is=(InputStream)ep.getNote( isNote );
+- int pos = 0;
+- int got;
+-
+- while(pos < len) {
+- try {
+- got = is.read(b, pos + offset, len - pos);
+- } catch(ClosedChannelException sex) {
+- if(pos > 0) {
+- log.info("Error reading data after
"+pos+"bytes",sex);
+- } else {
+- log.debug("Error reading data", sex);
+- }
+- got = -1;
+- }
+- if (log.isTraceEnabled()) {
+- log.trace("read() " + b + " " + (b==null ? 0:
b.length) + " " +
+- offset + " " + len + " = " + got );
+- }
+-
+- // connection just closed by remote.
+- if (got <= 0) {
+- // This happens periodically, as apache restarts
+- // periodically.
+- // It should be more gracefull ! - another feature for Ajp14
+- // log.warn( "server has closed the current connection (-1)"
);
+- return -3;
+- }
+-
+- pos += got;
+- }
+- return pos;
+- }
+-
+- protected boolean running=true;
+-
+- /** Accept incoming connections, dispatch to the thread pool
+- */
+- void acceptConnections() {
+- if( running ) {
+- try{
+- MsgContext ep=createMsgContext();
+- ep.setSource(this);
+- ep.setWorkerEnv( wEnv );
+- this.accept(ep);
+-
+- if( !running ) return;
+-
+- // Since this is a long-running connection, we don't care
+- // about the small GC
+- SocketConnection ajpConn=
+- new SocketConnection( ep);
+- ajpConn.register(ep);
+- }catch(Exception ex) {
+- if (running)
+- log.warn("Exception executing accept" ,ex);
+- }
+- }
+- }
+-
+-
+- // XXX This should become handleNotification
+- public int invoke( Msg msg, MsgContext ep ) throws IOException {
+- int type=ep.getType();
+-
+- switch( type ) {
+- case JkHandler.HANDLE_RECEIVE_PACKET:
+- if( log.isDebugEnabled()) log.debug("RECEIVE_PACKET ?? ");
+- return receive( msg, ep );
+- case JkHandler.HANDLE_SEND_PACKET:
+- return send( msg, ep );
+- case JkHandler.HANDLE_FLUSH:
+- return flush( msg, ep );
+- }
+-
+- if( log.isTraceEnabled() )
+- log.trace("Call next " + type + " " + next);
+-
+- // Send notification
+- if( nSupport!=null ) {
+- Notification notif=(Notification)ep.getNote(notifNote);
+- if( notif==null ) {
+- notif=new Notification("channelNioSocket.message", ep,
requestCount );
+- ep.setNote( notifNote, notif);
+- }
+- nSupport.sendNotification(notif);
+- }
+-
+- if( next != null ) {
+- return next.invoke( msg, ep );
+- } else {
+- log.info("No next ");
+- }
+-
+- return OK;
+- }
+-
+- public boolean isSameAddress(MsgContext ep) {
+- Socket s=(Socket)ep.getNote( socketNote );
+- return isSameAddress( s.getLocalAddress(), s.getInetAddress());
+- }
+-
+- public String getChannelName() {
+- String encodedAddr = "";
+- if (inet != null && !"0.0.0.0".equals(inet.getHostAddress()))
{
+- encodedAddr = getAddress();
+- if (encodedAddr.startsWith("/"))
+- encodedAddr = encodedAddr.substring(1);
+- encodedAddr = URLEncoder.encode(encodedAddr) + "-";
+- }
+- return ("jk-" + encodedAddr + port);
+- }
+-
+- /**
+- * Return <code>true</code> if the specified client and server
addresses
+- * are the same. This method works around a bug in the IBM 1.1.8 JVM on
+- * Linux, where the address bytes are returned reversed in some
+- * circumstances.
+- *
+- * @param server The server's InetAddress
+- * @param client The client's InetAddress
+- */
+- public static boolean isSameAddress(InetAddress server, InetAddress client)
+- {
+- // Compare the byte array versions of the two addresses
+- byte serverAddr[] = server.getAddress();
+- byte clientAddr[] = client.getAddress();
+- if (serverAddr.length != clientAddr.length)
+- return (false);
+- boolean match = true;
+- for (int i = 0; i < serverAddr.length; i++) {
+- if (serverAddr[i] != clientAddr[i]) {
+- match = false;
+- break;
+- }
+- }
+- if (match)
+- return (true);
+-
+- // Compare the reversed form of the two addresses
+- for (int i = 0; i < serverAddr.length; i++) {
+- if (serverAddr[i] != clientAddr[(serverAddr.length-1)-i])
+- return (false);
+- }
+- return (true);
+- }
+-
+- public void sendNewMessageNotification(Notification notification) {
+- if( nSupport!= null )
+- nSupport.sendNotification(notification);
+- }
+-
+- private NotificationBroadcasterSupport nSupport= null;
+-
+- public void addNotificationListener(NotificationListener listener,
+- NotificationFilter filter,
+- Object handback)
+- throws IllegalArgumentException
+- {
+- if( nSupport==null ) nSupport=new NotificationBroadcasterSupport();
+- nSupport.addNotificationListener(listener, filter, handback);
+- }
+-
+- public void removeNotificationListener(NotificationListener listener)
+- throws ListenerNotFoundException
+- {
+- if( nSupport!=null)
+- nSupport.removeNotificationListener(listener);
+- }
+-
+- MBeanNotificationInfo notifInfo[]=new MBeanNotificationInfo[0];
+-
+- public void setNotificationInfo( MBeanNotificationInfo info[]) {
+- this.notifInfo=info;
+- }
+-
+- public MBeanNotificationInfo[] getNotificationInfo() {
+- return notifInfo;
+- }
+-
+- protected class SocketConnection implements ThreadPoolRunnable {
+- MsgContext ep;
+- MsgAjp recv = new MsgAjp(packetSize);
+- boolean inProgress = false;
+-
+- SocketConnection(MsgContext ep) {
+- this.ep=ep;
+- }
+-
+- public Object[] getInitData() {
+- return null;
+- }
+-
+- public void runIt(Object perTh[]) {
+- if(!processConnection(ep)) {
+- unregister(ep);
+- }
+- }
+-
+- public boolean isRunning() {
+- return inProgress;
+- }
+-
+- public void setFinished() {
+- inProgress = false;
+- }
+-
+- /** Process a single ajp connection.
+- */
+- boolean processConnection(MsgContext ep) {
+- try {
+- InputStream sis = (InputStream)ep.getNote(isNote);
+- boolean haveInput = true;
+- while(haveInput) {
+- if( !running || paused ) {
+- return false;
+- }
+- int status= receive( recv, ep );
+- if( status <= 0 ) {
+- if( status==-3)
+- log.debug( "server has been restarted or reset this
connection" );
+- else
+- log.warn("Closing ajp connection " + status );
+- return false;
+- }
+- ep.setLong( MsgContext.TIMER_RECEIVED, System.currentTimeMillis());
+-
+- ep.setType( 0 );
+- // Will call next
+- status= invoke( recv, ep );
+- if( status != JkHandler.OK ) {
+- log.warn("processCallbacks status " + status );
+- return false;
+- }
+- synchronized(this) {
+- synchronized(sis) {
+- haveInput = sis.available() > 0;
+- }
+- if(!haveInput) {
+- setFinished();
+- } else {
+- if(log.isDebugEnabled())
+- log.debug("KeepAlive: "+sis.available());
+- }
+- }
+- }
+- } catch( Exception ex ) {
+- String msg = ex.getMessage();
+- if( msg != null && msg.indexOf( "Connection reset" )
>= 0)
+- log.debug( "Server has been restarted or reset this
connection");
+- else if (msg != null && msg.indexOf( "Read timed out"
) >=0 )
+- log.debug( "connection timeout reached");
+- else
+- log.error( "Error, processing connection", ex);
+- return false;
+- }
+- return true;
+- }
+-
+- synchronized void process(SelectionKey sk) {
+- if(!sk.isValid()) {
+- SocketInputStream sis = (SocketInputStream)ep.getNote(isNote);
+- sis.closeIt();
+- return;
+- }
+- if(sk.isReadable()) {
+- SocketInputStream sis = (SocketInputStream)ep.getNote(isNote);
+- boolean isok = sis.readAvailable();
+- if(!inProgress) {
+- if(isok) {
+- if(sis.available() > 0 || !nioIsBroken){
+- inProgress = true;
+- tp.runIt(this);
+- }
+- } else {
+- unregister(ep);
+- return;
+- }
+- }
+- }
+- if(sk.isWritable()) {
+- Object os = ep.getNote(osNote);
+- synchronized(os) {
+- os.notify();
+- }
+- }
+- }
+-
+- synchronized void unregister(MsgContext ep) {
+- try{
+- close(ep);
+- } catch(Exception e) {
+- log.error("Error closing connection", e);
+- }
+- try{
+- Request req = (Request)ep.getRequest();
+- if( req != null ) {
+- ObjectName roname = (ObjectName)ep.getNote(JMXRequestNote);
+- if( roname != null ) {
+- Registry.getRegistry(null, null).unregisterComponent(roname);
+- }
+- req.getRequestProcessor().setGlobalProcessor(null);
+- }
+- } catch( Exception ee) {
+- log.error( "Error, releasing connection",ee);
+- }
+- }
+-
+- void register(MsgContext ep) {
+- Socket s = (Socket)ep.getNote(socketNote);
+- try {
+- s.getChannel().register(selector, SelectionKey.OP_READ, this);
+- } catch(IOException iex) {
+- log.error("Unable to register connection",iex);
+- unregister(ep);
+- }
+- }
+-
+- }
+-
+- protected class Poller implements ThreadPoolRunnable {
+-
+- Poller() {
+- }
+-
+- public Object[] getInitData() {
+- return null;
+- }
+-
+- public void runIt(Object perTh[]) {
+- while(running) {
+- try {
+- int ns = selector.select(serverTimeout);
+- if(log.isDebugEnabled())
+- log.debug("Selecting "+ns+" channels");
+- if(ns > 0) {
+- Set sels = selector.selectedKeys();
+- Iterator it = sels.iterator();
+- while(it.hasNext()) {
+- SelectionKey sk = (SelectionKey)it.next();
+- if(sk.isAcceptable()) {
+- acceptConnections();
+- } else {
+- SocketConnection sc =
(SocketConnection)sk.attachment();
+- sc.process(sk);
+- }
+- it.remove();
+- }
+- }
+- } catch(ClosedSelectorException cse) {
+- log.debug("Selector is closed");
+- return;
+- } catch(CancelledKeyException cke) {
+- log.debug("Key Cancelled", cke);
+- } catch(IOException iex) {
+- log.warn("IO Error in select",iex);
+- } catch(Exception ex) {
+- log.warn("Error processing select",ex);
+- }
+- }
+- }
+- }
+-
+- protected class SocketInputStream extends InputStream {
+- final int BUFFER_SIZE = 8200;
+- private ByteBuffer buffer = ByteBuffer.allocateDirect(BUFFER_SIZE);
+- private SocketChannel channel;
+- private boolean blocking = false;
+- private boolean isClosed = false;
+- private volatile boolean dataAvailable = false;
+-
+- SocketInputStream(SocketChannel channel) {
+- this.channel = channel;
+- buffer.limit(0);
+- }
+-
+- public int available() {
+- return buffer.remaining();
+- }
+-
+- public void mark(int readlimit) {
+- buffer.mark();
+- }
+-
+- public boolean markSupported() {
+- return true;
+- }
+-
+- public void reset() {
+- buffer.reset();
+- }
+-
+- public synchronized int read() throws IOException {
+- if(!checkAvailable(1)) {
+- block(1);
+- }
+- return buffer.get();
+- }
+-
+- private boolean checkAvailable(int nbyte) throws IOException {
+- if(isClosed) {
+- throw new ClosedChannelException();
+- }
+- return buffer.remaining() >= nbyte;
+- }
+-
+- private int fill(int nbyte) throws IOException {
+- int rem = nbyte;
+- int read = 0;
+- boolean eof = false;
+- byte [] oldData = null;
+- if(buffer.remaining() > 0) {
+- // should rarely happen, so short-lived GC shouldn't hurt
+- // as much as allocating a long-lived buffer for this
+- if(log.isDebugEnabled())
+- log.debug("Saving old buffer: "+buffer.remaining());
+- oldData = new byte[buffer.remaining()];
+- buffer.get(oldData);
+- }
+- buffer.clear();
+- if(oldData != null) {
+- buffer.put(oldData);
+- }
+- while(rem > 0) {
+- int count = channel.read(buffer);
+- if(count < 0) {
+- eof = true;
+- break;
+- } else if(count == 0) {
+- log.debug("Failed to recieve signaled read: ");
+- break;
+- }
+- read += count;
+- rem -= count;
+- }
+- buffer.flip();
+- return eof ? -1 : read;
+- }
+-
+- synchronized boolean readAvailable() {
+- if(blocking) {
+- dataAvailable = true;
+- notify();
+- } else if(dataAvailable) {
+- log.debug("Race Condition");
+- } else {
+- int nr=0;
+-
+- try {
+- nr = fill(1);
+- } catch(ClosedChannelException cce) {
+- log.debug("Channel is closed",cce);
+- nr = -1;
+- } catch(IOException iex) {
+- log.warn("Exception processing read",iex);
+- nr = -1; // Can't handle this yet
+- }
+- if(nr < 0) {
+- closeIt();
+- return false;
+- } else if(nr == 0) {
+- if(!nioIsBroken) {
+- dataAvailable = (buffer.remaining() <= 0);
+- }
+- }
+- }
+- return true;
+- }
+-
+- synchronized void closeIt() {
+- isClosed = true;
+- if(blocking)
+- notify();
+- }
+-
+- public int read(byte [] data) throws IOException {
+- return read(data, 0, data.length);
+- }
+-
+- public synchronized int read(byte [] data, int offset, int len) throws
IOException {
+- int olen = len;
+- while(!checkAvailable(len)) {
+- int avail = buffer.remaining();
+- if(avail > 0) {
+- buffer.get(data, offset, avail);
+- }
+- len -= avail;
+- offset += avail;
+- block(len);
+- }
+- buffer.get(data, offset, len);
+- return olen;
+- }
+-
+- private void block(int len) throws IOException {
+- if(len <= 0) {
+- return;
+- }
+- if(!dataAvailable) {
+- blocking = true;
+- if(log.isDebugEnabled())
+- log.debug("Waiting for "+len+" bytes to be
available");
+- try{
+- wait(socketTimeout);
+- }catch(InterruptedException iex) {
+- log.debug("Interrupted",iex);
+- }
+- blocking = false;
+- }
+- if(dataAvailable) {
+- dataAvailable = false;
+- if(fill(len) < 0) {
+- isClosed = true;
+- }
+- } else if(!isClosed) {
+- throw new SocketTimeoutException("Read request timed out");
+- }
+- }
+- }
+-
+- protected class SocketOutputStream extends OutputStream {
+- ByteBuffer buffer = ByteBuffer.allocateDirect(bufferSize);
+- SocketChannel channel;
+-
+- SocketOutputStream(SocketChannel channel) {
+- this.channel = channel;
+- }
+-
+- public void write(int b) throws IOException {
+- if(!checkAvailable(1)) {
+- flush();
+- }
+- buffer.put((byte)b);
+- }
+-
+- public void write(byte [] data) throws IOException {
+- write(data, 0, data.length);
+- }
+-
+- public void write(byte [] data, int offset, int len) throws IOException {
+- if(!checkAvailable(len)) {
+- flush();
+- }
+- buffer.put(data, offset, len);
+- }
+-
+- public void flush() throws IOException {
+- buffer.flip();
+- while(buffer.hasRemaining()) {
+- int count = channel.write(buffer);
+- if(count == 0) {
+- synchronized(this) {
+- SelectionKey key = channel.keyFor(selector);
+- key.interestOps(SelectionKey.OP_WRITE);
+- if(log.isDebugEnabled())
+- log.debug("Blocking for channel write:
"+buffer.remaining());
+- try {
+- wait();
+- } catch(InterruptedException iex) {
+- // ignore, since can't happen
+- }
+- key.interestOps(SelectionKey.OP_READ);
+- }
+- }
+- }
+- buffer.clear();
+- }
+-
+- private boolean checkAvailable(int len) {
+- return buffer.remaining() >= len;
+- }
+- }
+-
+-}
+-
+Index: java/org/apache/tomcat/util/net/NioBlockingSelector.java
+===================================================================
+--- java/org/apache/tomcat/util/net/NioBlockingSelector.java (revision 590752)
++++ java/org/apache/tomcat/util/net/NioBlockingSelector.java (working copy)
+@@ -1,161 +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.EOFException;
+-import java.io.IOException;
+-import java.net.SocketTimeoutException;
+-import java.nio.ByteBuffer;
+-import java.nio.channels.SelectionKey;
+-import java.util.concurrent.TimeUnit;
+-
+-import org.apache.tomcat.util.net.NioEndpoint.KeyAttachment;
+-
+-public class NioBlockingSelector {
+- public NioBlockingSelector() {
+- }
+-
+- /**
+- * Performs a blocking write using the bytebuffer for data to be written
+- * If the <code>selector</code> parameter is null, then it will perform
a busy write that could
+- * take up a lot of CPU cycles.
+- * @param buf ByteBuffer - the buffer containing the data, we will write as long as
<code>(buf.hasRemaining()==true)</code>
+- * @param socket SocketChannel - the socket to write data to
+- * @param writeTimeout long - the timeout for this write operation in milliseconds,
-1 means no timeout
+- * @return int - returns the number of bytes written
+- * @throws EOFException if write returns -1
+- * @throws SocketTimeoutException if the write times out
+- * @throws IOException if an IO Exception occurs in the underlying socket logic
+- */
+- public static int write(ByteBuffer buf, NioChannel socket, long writeTimeout) throws
IOException {
+- SelectionKey key =
socket.getIOChannel().keyFor(socket.getPoller().getSelector());
+- int written = 0;
+- boolean timedout = false;
+- int keycount = 1; //assume we can write
+- long time = System.currentTimeMillis(); //start the timeout timer
+- try {
+- while ( (!timedout) && buf.hasRemaining()) {
+- if (keycount > 0) { //only write if we were registered for a write
+- int cnt = socket.write(buf); //write the data
+- if (cnt == -1)
+- throw new EOFException();
+- written += cnt;
+- if (cnt > 0) {
+- time = System.currentTimeMillis(); //reset our timeout timer
+- continue; //we successfully wrote, try again without a selector
+- }
+- }
+- if ( key == null ) throw new IOException("Key no longer
registered");
+- KeyAttachment att = (KeyAttachment) key.attachment();
+- try {
+- if ( att.getWriteLatch()==null || att.getWriteLatch().getCount()==0)
att.startWriteLatch(1);
+- //only register for write if a write has not yet been issued
+- if ( (att.interestOps() & SelectionKey.OP_WRITE) == 0)
socket.getPoller().add(socket,SelectionKey.OP_WRITE);
+- att.awaitWriteLatch(writeTimeout,TimeUnit.MILLISECONDS);
+- }catch (InterruptedException ignore) {
+- Thread.interrupted();
+- }
+- if ( att.getWriteLatch()!=null &&
att.getWriteLatch().getCount()> 0) {
+- //we got interrupted, but we haven't received notification from
the poller.
+- keycount = 0;
+- }else {
+- //latch countdown has happened
+- keycount = 1;
+- att.resetWriteLatch();
+- }
+-
+- if (writeTimeout > 0 && (keycount == 0))
+- timedout = (System.currentTimeMillis() - time) >= writeTimeout;
+- } //while
+- if (timedout)
+- throw new SocketTimeoutException();
+- } finally {
+- if (timedout && key != null) {
+- cancelKey(socket, key);
+- }
+- }
+- return written;
+- }
+-
+- private static void cancelKey(final NioChannel socket, final SelectionKey key) {
+- socket.getPoller().addEvent(
+- new Runnable() {
+- public void run() {
+- key.cancel();
+- }
+- });
+- }
+-
+- /**
+- * Performs a blocking read using the bytebuffer for data to be read
+- * If the <code>selector</code> parameter is null, then it will perform
a busy read that could
+- * take up a lot of CPU cycles.
+- * @param buf ByteBuffer - the buffer containing the data, we will read as until we
have read at least one byte or we timed out
+- * @param socket SocketChannel - the socket to write data to
+- * @param selector Selector - the selector to use for blocking, if null then a busy
read will be initiated
+- * @param readTimeout long - the timeout for this read operation in milliseconds, -1
means no timeout
+- * @return int - returns the number of bytes read
+- * @throws EOFException if read returns -1
+- * @throws SocketTimeoutException if the read times out
+- * @throws IOException if an IO Exception occurs in the underlying socket logic
+- */
+- public static int read(ByteBuffer buf, NioChannel socket, long readTimeout) throws
IOException {
+- final SelectionKey key =
socket.getIOChannel().keyFor(socket.getPoller().getSelector());
+- int read = 0;
+- boolean timedout = false;
+- int keycount = 1; //assume we can write
+- long time = System.currentTimeMillis(); //start the timeout timer
+- try {
+- while ( (!timedout) && read == 0) {
+- if (keycount > 0) { //only read if we were registered for a read
+- int cnt = socket.read(buf);
+- if (cnt == -1)
+- throw new EOFException();
+- read += cnt;
+- if (cnt > 0)
+- break;
+- }
+- KeyAttachment att = (KeyAttachment) key.attachment();
+- try {
+- if ( att.getReadLatch()==null || att.getReadLatch().getCount()==0)
att.startReadLatch(1);
+- if ( att.interestOps() == 0)
socket.getPoller().add(socket,SelectionKey.OP_READ);
+- att.awaitReadLatch(readTimeout,TimeUnit.MILLISECONDS);
+- }catch (InterruptedException ignore) {
+- Thread.interrupted();
+- }
+- if ( att.getReadLatch()!=null &&
att.getReadLatch().getCount()> 0) {
+- //we got interrupted, but we haven't received notification from
the poller.
+- keycount = 0;
+- }else {
+- //latch countdown has happened
+- keycount = 1;
+- att.resetReadLatch();
+- }
+- if (readTimeout > 0 && (keycount == 0))
+- timedout = (System.currentTimeMillis() - time) >= readTimeout;
+- } //while
+- if (timedout)
+- throw new SocketTimeoutException();
+- } finally {
+- if (timedout && key != null) {
+- cancelKey(socket,key);
+- }
+- }
+- return read;
+- }
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/tomcat/util/net/NioChannel.java
+===================================================================
+--- java/org/apache/tomcat/util/net/NioChannel.java (revision 590752)
++++ java/org/apache/tomcat/util/net/NioChannel.java (working copy)
+@@ -1,193 +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.nio.ByteBuffer;
+-import java.nio.channels.ByteChannel;
+-import java.nio.channels.SocketChannel;
+-
+-import org.apache.tomcat.util.net.NioEndpoint.Poller;
+-import org.apache.tomcat.util.net.SecureNioChannel.ApplicationBufferHandler;
+-import java.nio.channels.Selector;
+-import java.nio.channels.SelectionKey;
+-
+-/**
+- *
+- * Base class for a SocketChannel wrapper used by the endpoint.
+- * This way, logic for a SSL socket channel remains the same as for
+- * a non SSL, making sure we don't need to code for any exception cases.
+- *
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-public class NioChannel implements ByteChannel{
+-
+- protected static ByteBuffer emptyBuf = ByteBuffer.allocate(0);
+-
+- protected SocketChannel sc = null;
+-
+- protected ApplicationBufferHandler bufHandler;
+-
+- protected Poller poller;
+-
+- public NioChannel(SocketChannel channel, ApplicationBufferHandler bufHandler) throws
IOException {
+- this.sc = channel;
+- this.bufHandler = bufHandler;
+- }
+-
+- public void reset() throws IOException {
+- bufHandler.getReadBuffer().clear();
+- bufHandler.getWriteBuffer().clear();
+- }
+-
+- public int getBufferSize() {
+- if ( bufHandler == null ) return 0;
+- int size = 0;
+- size +=
bufHandler.getReadBuffer()!=null?bufHandler.getReadBuffer().capacity():0;
+- size +=
bufHandler.getWriteBuffer()!=null?bufHandler.getWriteBuffer().capacity():0;
+- return size;
+- }
+-
+- /**
+- * returns true if the network buffer has
+- * been flushed out and is empty
+- * @return boolean
+- */
+- public boolean flush(boolean block, Selector s,long timeout) throws IOException {
+- return true; //no network buffer in the regular channel
+- }
+-
+-
+- /**
+- * Closes this channel.
+- *
+- * @throws IOException If an I/O error occurs
+- * @todo Implement this java.nio.channels.Channel method
+- */
+- public void close() throws IOException {
+- getIOChannel().socket().close();
+- getIOChannel().close();
+- }
+-
+- public void close(boolean force) throws IOException {
+- if (isOpen() || force ) close();
+- }
+- /**
+- * Tells whether or not this channel is open.
+- *
+- * @return <tt>true</tt> if, and only if, this channel is open
+- * @todo Implement this java.nio.channels.Channel method
+- */
+- public boolean isOpen() {
+- return sc.isOpen();
+- }
+-
+- /**
+- * Writes a sequence of bytes to this channel from the given buffer.
+- *
+- * @param src The buffer from which bytes are to be retrieved
+- * @return The number of bytes written, possibly zero
+- * @throws IOException If some other I/O error occurs
+- * @todo Implement this java.nio.channels.WritableByteChannel method
+- */
+- public int write(ByteBuffer src) throws IOException {
+- return sc.write(src);
+- }
+-
+- /**
+- * Reads a sequence of bytes from this channel into the given buffer.
+- *
+- * @param dst The buffer into which bytes are to be transferred
+- * @return The number of bytes read, possibly zero, or <tt>-1</tt> if
the channel has reached end-of-stream
+- * @throws IOException If some other I/O error occurs
+- * @todo Implement this java.nio.channels.ReadableByteChannel method
+- */
+- public int read(ByteBuffer dst) throws IOException {
+- return sc.read(dst);
+- }
+-
+- public Object getAttachment(boolean remove) {
+- Poller pol = getPoller();
+- Selector sel = pol!=null?pol.getSelector():null;
+- SelectionKey key = sel!=null?getIOChannel().keyFor(sel):null;
+- Object att = key!=null?key.attachment():null;
+- if (key != null && att != null && remove ) key.attach(null);
+- return att;
+- }
+- /**
+- * getBufHandler
+- *
+- * @return ApplicationBufferHandler
+- * @todo Implement this org.apache.tomcat.util.net.SecureNioChannel method
+- */
+- public ApplicationBufferHandler getBufHandler() {
+- return bufHandler;
+- }
+-
+- public Poller getPoller() {
+- return poller;
+- }
+- /**
+- * getIOChannel
+- *
+- * @return SocketChannel
+- * @todo Implement this org.apache.tomcat.util.net.SecureNioChannel method
+- */
+- public SocketChannel getIOChannel() {
+- return sc;
+- }
+-
+- /**
+- * isClosing
+- *
+- * @return boolean
+- * @todo Implement this org.apache.tomcat.util.net.SecureNioChannel method
+- */
+- public boolean isClosing() {
+- return false;
+- }
+-
+- /**
+- * isInitHandshakeComplete
+- *
+- * @return boolean
+- * @todo Implement this org.apache.tomcat.util.net.SecureNioChannel method
+- */
+- public boolean isInitHandshakeComplete() {
+- return true;
+- }
+-
+- public int handshake(boolean read, boolean write) throws IOException {
+- return 0;
+- }
+-
+- public void setPoller(Poller poller) {
+- this.poller = poller;
+- }
+-
+- public void setIOChannel(SocketChannel IOChannel) {
+- this.sc = IOChannel;
+- }
+-
+- public String toString() {
+- return super.toString()+":"+this.sc.toString();
+- }
+-
+-}
+Index: java/org/apache/tomcat/util/net/NioSelectorPool.java
+===================================================================
+--- java/org/apache/tomcat/util/net/NioSelectorPool.java (revision 590752)
++++ java/org/apache/tomcat/util/net/NioSelectorPool.java (working copy)
+@@ -1,264 +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.util.concurrent.atomic.AtomicInteger;
+-import java.nio.channels.Selector;
+-import java.io.IOException;
+-import java.util.NoSuchElementException;
+-import java.nio.ByteBuffer;
+-import java.nio.channels.SelectionKey;
+-import java.io.EOFException;
+-import java.net.SocketTimeoutException;
+-import java.util.concurrent.ConcurrentLinkedQueue;
+-import org.apache.juli.logging.Log;
+-import org.apache.juli.logging.LogFactory;
+-
+-/**
+- *
+- * Thread safe non blocking selector pool
+- * @author Filip Hanik
+- * @version 1.0
+- * @since 6.0
+- */
+-
+-public class NioSelectorPool {
+- protected static Log log = LogFactory.getLog(NioSelectorPool.class);
+-
+- protected final static boolean SHARED =
+-
Boolean.valueOf(System.getProperty("org.apache.tomcat.util.net.NioSelectorShared",
"true")).booleanValue();
+- protected static Selector SHARED_SELECTOR;
+-
+- protected int maxSelectors = 200;
+- protected int maxSpareSelectors = -1;
+- protected boolean enabled = true;
+- protected AtomicInteger active = new AtomicInteger(0);
+- protected AtomicInteger spare = new AtomicInteger(0);
+- protected ConcurrentLinkedQueue<Selector> selectors = new
ConcurrentLinkedQueue<Selector>();
+-
+- protected static Selector getSharedSelector() throws IOException {
+- if (SHARED && SHARED_SELECTOR == null) {
+- synchronized ( NioSelectorPool.class ) {
+- if ( SHARED_SELECTOR == null ) {
+- SHARED_SELECTOR = Selector.open();
+- log.info("Using a shared selector for servlet
write/read");
+- }
+- }
+- }
+- return SHARED_SELECTOR;
+- }
+-
+- public Selector get() throws IOException{
+- if ( SHARED ) {
+- return getSharedSelector();
+- }
+- if ( (!enabled) || active.incrementAndGet() >= maxSelectors ) {
+- if ( enabled ) active.decrementAndGet();
+- return null;
+- }
+- Selector s = null;
+- try {
+- s = selectors.size()>0?selectors.poll():null;
+- if (s == null) s = Selector.open();
+- else spare.decrementAndGet();
+-
+- }catch (NoSuchElementException x ) {
+- try {s = Selector.open();}catch (IOException iox){}
+- } finally {
+- if ( s == null ) active.decrementAndGet();//we were unable to find a
selector
+- }
+- return s;
+- }
+-
+-
+-
+- public void put(Selector s) throws IOException {
+- if ( SHARED ) return;
+- if ( enabled ) active.decrementAndGet();
+- if ( enabled && (maxSpareSelectors==-1 || spare.get() <
Math.min(maxSpareSelectors,maxSelectors)) ) {
+- spare.incrementAndGet();
+- selectors.offer(s);
+- }
+- else s.close();
+- }
+-
+- public void close() throws IOException {
+- enabled = false;
+- Selector s;
+- while ( (s = selectors.poll()) != null ) s.close();
+- spare.set(0);
+- active.set(0);
+- if ( SHARED && getSharedSelector()!=null ) {
+- getSharedSelector().close();
+- SHARED_SELECTOR = null;
+- }
+- }
+-
+- public void open() throws IOException {
+- enabled = true;
+- getSharedSelector();
+- }
+-
+- /**
+- * Performs a blocking write using the bytebuffer for data to be written and a
selector to block.
+- * If the <code>selector</code> parameter is null, then it will perform
a busy write that could
+- * take up a lot of CPU cycles.
+- * @param buf ByteBuffer - the buffer containing the data, we will write as long as
<code>(buf.hasRemaining()==true)</code>
+- * @param socket SocketChannel - the socket to write data to
+- * @param selector Selector - the selector to use for blocking, if null then a busy
write will be initiated
+- * @param writeTimeout long - the timeout for this write operation in milliseconds,
-1 means no timeout
+- * @return int - returns the number of bytes written
+- * @throws EOFException if write returns -1
+- * @throws SocketTimeoutException if the write times out
+- * @throws IOException if an IO Exception occurs in the underlying socket logic
+- */
+- public int write(ByteBuffer buf, NioChannel socket, Selector selector, long
writeTimeout) throws IOException {
+- return write(buf,socket,selector,writeTimeout,true);
+- }
+-
+- public int write(ByteBuffer buf, NioChannel socket, Selector selector, long
writeTimeout, boolean block) throws IOException {
+- if ( SHARED && block) {
+- return NioBlockingSelector.write(buf,socket,writeTimeout);
+- }
+- SelectionKey key = null;
+- int written = 0;
+- boolean timedout = false;
+- int keycount = 1; //assume we can write
+- long time = System.currentTimeMillis(); //start the timeout timer
+- try {
+- while ( (!timedout) && buf.hasRemaining() ) {
+- int cnt = 0;
+- if ( keycount > 0 ) { //only write if we were registered for a write
+- cnt = socket.write(buf); //write the data
+- if (cnt == -1) throw new EOFException();
+- written += cnt;
+- if (cnt > 0) {
+- time = System.currentTimeMillis(); //reset our timeout timer
+- continue; //we successfully wrote, try again without a selector
+- }
+- if (cnt==0 && (!block)) break; //don't block
+- }
+- if ( selector != null ) {
+- //register OP_WRITE to the selector
+- if (key==null) key = socket.getIOChannel().register(selector,
SelectionKey.OP_WRITE);
+- else key.interestOps(SelectionKey.OP_WRITE);
+- keycount = selector.select(writeTimeout);
+- }
+- if (writeTimeout > 0 && (selector == null || keycount == 0) )
timedout = (System.currentTimeMillis()-time)>=writeTimeout;
+- }//while
+- if ( timedout ) throw new SocketTimeoutException();
+- } finally {
+- if (key != null) {
+- key.cancel();
+- if (selector != null) selector.selectNow();//removes the key from this
selector
+- }
+- }
+- return written;
+- }
+-
+- /**
+- * Performs a blocking read using the bytebuffer for data to be read and a selector
to block.
+- * If the <code>selector</code> parameter is null, then it will perform
a busy read that could
+- * take up a lot of CPU cycles.
+- * @param buf ByteBuffer - the buffer containing the data, we will read as until we
have read at least one byte or we timed out
+- * @param socket SocketChannel - the socket to write data to
+- * @param selector Selector - the selector to use for blocking, if null then a busy
read will be initiated
+- * @param readTimeout long - the timeout for this read operation in milliseconds, -1
means no timeout
+- * @return int - returns the number of bytes read
+- * @throws EOFException if read returns -1
+- * @throws SocketTimeoutException if the read times out
+- * @throws IOException if an IO Exception occurs in the underlying socket logic
+- */
+- public int read(ByteBuffer buf, NioChannel socket, Selector selector, long
readTimeout) throws IOException {
+- return read(buf,socket,selector,readTimeout,true);
+- }
+- /**
+- * Performs a read using the bytebuffer for data to be read and a selector to
register for events should
+- * you have the block=true.
+- * If the <code>selector</code> parameter is null, then it will perform
a busy read that could
+- * take up a lot of CPU cycles.
+- * @param buf ByteBuffer - the buffer containing the data, we will read as until we
have read at least one byte or we timed out
+- * @param socket SocketChannel - the socket to write data to
+- * @param selector Selector - the selector to use for blocking, if null then a busy
read will be initiated
+- * @param readTimeout long - the timeout for this read operation in milliseconds, -1
means no timeout
+- * @param block - true if you want to block until data becomes available or timeout
time has been reached
+- * @return int - returns the number of bytes read
+- * @throws EOFException if read returns -1
+- * @throws SocketTimeoutException if the read times out
+- * @throws IOException if an IO Exception occurs in the underlying socket logic
+- */
+- public int read(ByteBuffer buf, NioChannel socket, Selector selector, long
readTimeout, boolean block) throws IOException {
+- if ( SHARED && block) {
+- return NioBlockingSelector.read(buf,socket,readTimeout);
+- }
+- SelectionKey key = null;
+- int read = 0;
+- boolean timedout = false;
+- int keycount = 1; //assume we can write
+- long time = System.currentTimeMillis(); //start the timeout timer
+- try {
+- while ( (!timedout) ) {
+- int cnt = 0;
+- if ( keycount > 0 ) { //only read if we were registered for a read
+- cnt = socket.read(buf);
+- if (cnt == -1) throw new EOFException();
+- read += cnt;
+- if (cnt > 0) continue; //read some more
+- if (cnt==0 && (read>0 || (!block) ) ) break; //we are
done reading
+- }
+- if ( selector != null ) {//perform a blocking read
+- //register OP_WRITE to the selector
+- if (key==null) key = socket.getIOChannel().register(selector,
SelectionKey.OP_READ);
+- else key.interestOps(SelectionKey.OP_READ);
+- keycount = selector.select(readTimeout);
+- }
+- if (readTimeout > 0 && (selector == null || keycount == 0) )
timedout = (System.currentTimeMillis()-time)>=readTimeout;
+- }//while
+- if ( timedout ) throw new SocketTimeoutException();
+- } finally {
+- if (key != null) {
+- key.cancel();
+- if (selector != null) selector.selectNow();//removes the key from this
selector
+- }
+- }
+- return read;
+- }
+-
+- public void setMaxSelectors(int maxSelectors) {
+- this.maxSelectors = maxSelectors;
+- }
+-
+- public void setMaxSpareSelectors(int maxSpareSelectors) {
+- this.maxSpareSelectors = maxSpareSelectors;
+- }
+-
+- public void setEnabled(boolean enabled) {
+- this.enabled = enabled;
+- }
+-
+- public int getMaxSelectors() {
+- return maxSelectors;
+- }
+-
+- public int getMaxSpareSelectors() {
+- return maxSpareSelectors;
+- }
+-
+- public boolean isEnabled() {
+- return enabled;
+- }
+-}
+\ No newline at end of file
+Index: java/org/apache/tomcat/util/net/SecureNioChannel.java
+===================================================================
+--- java/org/apache/tomcat/util/net/SecureNioChannel.java (revision 590752)
++++ java/org/apache/tomcat/util/net/SecureNioChannel.java (working copy)
+@@ -1,473 +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.nio.ByteBuffer;
+-import java.nio.channels.SelectionKey;
+-import java.nio.channels.SocketChannel;
+-import javax.net.ssl.SSLEngine;
+-import javax.net.ssl.SSLEngineResult;
+-import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+-import javax.net.ssl.SSLEngineResult.Status;
+-import java.nio.channels.Selector;
+-
+-/**
+- *
+- * Implementation of a secure socket channel
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-
+-public class SecureNioChannel extends NioChannel {
+-
+- protected ByteBuffer netInBuffer;
+- protected ByteBuffer netOutBuffer;
+-
+- protected SSLEngine sslEngine;
+-
+- protected boolean initHandshakeComplete = false;
+- protected HandshakeStatus initHandshakeStatus; //gets set by begin handshake
+-
+- protected boolean closed = false;
+- protected boolean closing = false;
+-
+- protected NioSelectorPool pool;
+-
+- public SecureNioChannel(SocketChannel channel, SSLEngine engine,
+- ApplicationBufferHandler bufHandler, NioSelectorPool pool)
throws IOException {
+- super(channel,bufHandler);
+- this.sslEngine = engine;
+- int appBufSize = sslEngine.getSession().getApplicationBufferSize();
+- int netBufSize = sslEngine.getSession().getPacketBufferSize();
+- //allocate network buffers - TODO, add in optional direct non-direct buffers
+- if ( netInBuffer == null ) netInBuffer = ByteBuffer.allocateDirect(netBufSize);
+- if ( netOutBuffer == null ) netOutBuffer =
ByteBuffer.allocateDirect(netBufSize);
+-
+- //selector pool for blocking operations
+- this.pool = pool;
+-
+- //ensure that the application has a large enough read/write buffers
+- //by doing this, we should not encounter any buffer overflow errors
+- bufHandler.expand(bufHandler.getReadBuffer(), appBufSize);
+- bufHandler.expand(bufHandler.getWriteBuffer(), appBufSize);
+- reset();
+- }
+-
+- public void reset(SSLEngine engine) throws IOException {
+- this.sslEngine = engine;
+- reset();
+- }
+- public void reset() throws IOException {
+- super.reset();
+- netOutBuffer.position(0);
+- netOutBuffer.limit(0);
+- netInBuffer.position(0);
+- netInBuffer.limit(0);
+- initHandshakeComplete = false;
+- closed = false;
+- closing = false;
+- //initiate handshake
+- sslEngine.beginHandshake();
+- initHandshakeStatus = sslEngine.getHandshakeStatus();
+- }
+-
+- public int getBufferSize() {
+- int size = super.getBufferSize();
+- size += netInBuffer!=null?netInBuffer.capacity():0;
+- size += netOutBuffer!=null?netOutBuffer.capacity():0;
+- return size;
+- }
+-
+-
+-//===========================================================================================
+-// NIO SSL METHODS
+-//===========================================================================================
+- /**
+- * returns true if the network buffer has
+- * been flushed out and is empty
+- * @return boolean
+- */
+- public boolean flush(boolean block, Selector s, long timeout) throws IOException {
+- if (!block) {
+- flush(netOutBuffer);
+- } else {
+- pool.write(netOutBuffer, this, s, timeout);
+- }
+- return !netOutBuffer.hasRemaining();
+- }
+-
+- /**
+- * Flushes the buffer to the network, non blocking
+- * @param buf ByteBuffer
+- * @return boolean true if the buffer has been emptied out, false otherwise
+- * @throws IOException
+- */
+- protected boolean flush(ByteBuffer buf) throws IOException {
+- int remaining = buf.remaining();
+- if ( remaining > 0 ) {
+- int written = sc.write(buf);
+- return written >= remaining;
+- }else {
+- return true;
+- }
+- }
+-
+- /**
+- * Performs SSL handshake, non blocking, but performs NEED_TASK on the same
thread.<br>
+- * Hence, you should never call this method using your Acceptor thread, as you would
slow down
+- * your system significantly.<br>
+- * The return for this operation is 0 if the handshake is complete and a positive
value if it is not complete.
+- * In the event of a positive value coming back, reregister the selection key for
the return values interestOps.
+- * @param read boolean - true if the underlying channel is readable
+- * @param write boolean - true if the underlying channel is writable
+- * @return int - 0 if hand shake is complete, otherwise it returns a SelectionKey
interestOps value
+- * @throws IOException
+- */
+- public int handshake(boolean read, boolean write) throws IOException {
+- if ( initHandshakeComplete ) return 0; //we have done our initial handshake
+-
+- if (!flush(netOutBuffer)) return SelectionKey.OP_WRITE; //we still have data to
write
+-
+- SSLEngineResult handshake = null;
+-
+- while (!initHandshakeComplete) {
+- switch ( initHandshakeStatus ) {
+- case NOT_HANDSHAKING: {
+- //should never happen
+- throw new IOException("NOT_HANDSHAKING during
handshake");
+- }
+- case FINISHED: {
+- //we are complete if we have delivered the last package
+- initHandshakeComplete = !netOutBuffer.hasRemaining();
+- //return 0 if we are complete, otherwise we still have data to
write
+- return initHandshakeComplete?0:SelectionKey.OP_WRITE;
+- }
+- case NEED_WRAP: {
+- //perform the wrap function
+- handshake = handshakeWrap(write);
+- if ( handshake.getStatus() == Status.OK ){
+- if (initHandshakeStatus == HandshakeStatus.NEED_TASK)
+- initHandshakeStatus = tasks();
+- } else {
+- //wrap should always work with our buffers
+- throw new IOException("Unexpected status:" +
handshake.getStatus() + " during handshake WRAP.");
+- }
+- if ( initHandshakeStatus != HandshakeStatus.NEED_UNWRAP ||
(!flush(netOutBuffer)) ) {
+- //should actually return OP_READ if we have NEED_UNWRAP
+- return SelectionKey.OP_WRITE;
+- }
+- //fall down to NEED_UNWRAP on the same call, will result in a
+- //BUFFER_UNDERFLOW if it needs data
+- }
+- case NEED_UNWRAP: {
+- //perform the unwrap function
+- handshake = handshakeUnwrap(read);
+- if ( handshake.getStatus() == Status.OK ) {
+- if (initHandshakeStatus == HandshakeStatus.NEED_TASK)
+- initHandshakeStatus = tasks();
+- } else if ( handshake.getStatus() == Status.BUFFER_UNDERFLOW ){
+- //read more data, reregister for OP_READ
+- return SelectionKey.OP_READ;
+- } else {
+- throw new IOException("Invalid handshake
status:"+initHandshakeStatus+" during handshake UNWRAP.");
+- }//switch
+- break;
+- }
+- case NEED_TASK: {
+- initHandshakeStatus = tasks();
+- break;
+- }
+- default: throw new IllegalStateException("Invalid handshake
status:"+initHandshakeStatus);
+- }//switch
+- }//while
+- //return 0 if we are complete, otherwise reregister for any activity that
+- //would cause this method to be called again.
+- return initHandshakeComplete?0:(SelectionKey.OP_WRITE|SelectionKey.OP_READ);
+- }
+-
+- /**
+- * Executes all the tasks needed on the same thread.
+- * @return HandshakeStatus
+- */
+- protected SSLEngineResult.HandshakeStatus tasks() {
+- Runnable r = null;
+- while ( (r = sslEngine.getDelegatedTask()) != null) {
+- r.run();
+- }
+- return sslEngine.getHandshakeStatus();
+- }
+-
+- /**
+- * Performs the WRAP function
+- * @param doWrite boolean
+- * @return SSLEngineResult
+- * @throws IOException
+- */
+- protected SSLEngineResult handshakeWrap(boolean doWrite) throws IOException {
+- //this should never be called with a network buffer that contains data
+- //so we can clear it here.
+- netOutBuffer.clear();
+- //perform the wrap
+- SSLEngineResult result = sslEngine.wrap(bufHandler.getWriteBuffer(),
netOutBuffer);
+- //prepare the results to be written
+- netOutBuffer.flip();
+- //set the status
+- initHandshakeStatus = result.getHandshakeStatus();
+- //optimization, if we do have a writable channel, write it now
+- if ( doWrite ) flush(netOutBuffer);
+- return result;
+- }
+-
+- /**
+- * Perform handshake unwrap
+- * @param doread boolean
+- * @return SSLEngineResult
+- * @throws IOException
+- */
+- protected SSLEngineResult handshakeUnwrap(boolean doread) throws IOException {
+-
+- if (netInBuffer.position() == netInBuffer.limit()) {
+- //clear the buffer if we have emptied it out on data
+- netInBuffer.clear();
+- }
+- if ( doread ) {
+- //if we have data to read, read it
+- int read = sc.read(netInBuffer);
+- if (read == -1) throw new IOException("EOF encountered during
handshake.");
+- }
+- SSLEngineResult result;
+- boolean cont = false;
+- //loop while we can perform pure SSLEngine data
+- do {
+- //prepare the buffer with the incoming data
+- netInBuffer.flip();
+- //call unwrap
+- result = sslEngine.unwrap(netInBuffer, bufHandler.getReadBuffer());
+- //compact the buffer, this is an optional method, wonder what would happen
if we didn't
+- netInBuffer.compact();
+- //read in the status
+- initHandshakeStatus = result.getHandshakeStatus();
+- if ( result.getStatus() == SSLEngineResult.Status.OK &&
+- result.getHandshakeStatus() == HandshakeStatus.NEED_TASK ) {
+- //execute tasks if we need to
+- initHandshakeStatus = tasks();
+- }
+- //perform another unwrap?
+- cont = result.getStatus() == SSLEngineResult.Status.OK &&
+- initHandshakeStatus == HandshakeStatus.NEED_UNWRAP;
+- }while ( cont );
+- return result;
+- }
+-
+- /**
+- * Sends a SSL close message, will not physically close the connection
here.<br>
+- * To close the connection, you could do something like
+- * <pre><code>
+- * close();
+- * while (isOpen() && !myTimeoutFunction()) Thread.sleep(25);
+- * if ( isOpen() ) close(true); //forces a close if you timed out
+- * </code></pre>
+- * @throws IOException if an I/O error occurs
+- * @throws IOException if there is data on the outgoing network buffer and we are
unable to flush it
+- * @todo Implement this java.io.Closeable method
+- */
+- public void close() throws IOException {
+- if (closing) return;
+- closing = true;
+- sslEngine.closeOutbound();
+-
+- if (!flush(netOutBuffer)) {
+- throw new IOException("Remaining data in the network buffer, can't
send SSL close message, force a close with close(true) instead");
+- }
+- //prep the buffer for the close message
+- netOutBuffer.clear();
+- //perform the close, since we called sslEngine.closeOutbound
+- SSLEngineResult handshake = sslEngine.wrap(getEmptyBuf(), netOutBuffer);
+- //we should be in a close state
+- if (handshake.getStatus() != SSLEngineResult.Status.CLOSED) {
+- throw new IOException("Invalid close state, will not send network
data.");
+- }
+- //prepare the buffer for writing
+- netOutBuffer.flip();
+- //if there is data to be written
+- flush(netOutBuffer);
+-
+- //is the channel closed?
+- closed = (!netOutBuffer.hasRemaining() &&
(handshake.getHandshakeStatus() != HandshakeStatus.NEED_WRAP));
+- }
+-
+- /**
+- * Force a close, can throw an IOException
+- * @param force boolean
+- * @throws IOException
+- */
+- public void close(boolean force) throws IOException {
+- try {
+- close();
+- }finally {
+- if ( force || closed ) {
+- closed = true;
+- sc.socket().close();
+- sc.close();
+- }
+- }
+- }
+-
+- /**
+- * Reads a sequence of bytes from this channel into the given buffer.
+- *
+- * @param dst The buffer into which bytes are to be transferred
+- * @return The number of bytes read, possibly zero, or <tt>-1</tt> if
the channel has reached end-of-stream
+- * @throws IOException If some other I/O error occurs
+- * @throws IllegalArgumentException if the destination buffer is different than
bufHandler.getReadBuffer()
+- * @todo Implement this java.nio.channels.ReadableByteChannel method
+- */
+- public int read(ByteBuffer dst) throws IOException {
+- //if we want to take advantage of the expand function, make sure we only use the
ApplicationBufferHandler's buffers
+- if ( dst != bufHandler.getReadBuffer() ) throw new
IllegalArgumentException("You can only read using the application read buffer
provided by the handler.");
+- //are we in the middle of closing or closed?
+- if ( closing || closed) return -1;
+- //did we finish our handshake?
+- if (!initHandshakeComplete) throw new IllegalStateException("Handshake
incomplete, you must complete handshake before reading data.");
+-
+- //read from the network
+- int netread = sc.read(netInBuffer);
+- //did we reach EOF? if so send EOF up one layer.
+- if (netread == -1) return -1;
+-
+- //the data read
+- int read = 0;
+- //the SSL engine result
+- SSLEngineResult unwrap;
+- do {
+- //prepare the buffer
+- netInBuffer.flip();
+- //unwrap the data
+- unwrap = sslEngine.unwrap(netInBuffer, dst);
+- //compact the buffer
+- netInBuffer.compact();
+-
+- if ( unwrap.getStatus()==Status.OK ||
unwrap.getStatus()==Status.BUFFER_UNDERFLOW ) {
+- //we did receive some data, add it to our total
+- read += unwrap.bytesProduced();
+- //perform any tasks if needed
+- if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_TASK) tasks();
+- //if we need more network data, then bail out for now.
+- if ( unwrap.getStatus() == Status.BUFFER_UNDERFLOW ) break;
+- }else if ( unwrap.getStatus()==Status.BUFFER_OVERFLOW && read>0 )
{
+- //buffer overflow can happen, if we have read data, then
+- //empty out the dst buffer before we do another read
+- break;
+- }else {
+- //here we should trap BUFFER_OVERFLOW and call expand on the buffer
+- //for now, throw an exception, as we initialized the buffers
+- //in the constructor
+- throw new IOException("Unable to unwrap data, invalid status:
" + unwrap.getStatus());
+- }
+- } while ( (netInBuffer.position() != 0)); //continue to unwrapping as long as
the input buffer has stuff
+- return (read);
+- }
+-
+- /**
+- * Writes a sequence of bytes to this channel from the given buffer.
+- *
+- * @param src The buffer from which bytes are to be retrieved
+- * @return The number of bytes written, possibly zero
+- * @throws IOException If some other I/O error occurs
+- * @todo Implement this java.nio.channels.WritableByteChannel method
+- */
+- public int write(ByteBuffer src) throws IOException {
+- if ( src == this.netOutBuffer ) {
+- //we can get here through a recursive call
+- //by using the NioBlockingSelector
+- int written = sc.write(src);
+- return written;
+- } else {
+- //make sure we can handle expand, and that we only use on buffer
+- if ( src != bufHandler.getWriteBuffer() ) throw new
IllegalArgumentException("You can only write using the application write buffer
provided by the handler.");
+- //are we closing or closed?
+- if ( closing || closed) throw new IOException("Channel is in closing
state.");
+-
+- //the number of bytes written
+- int written = 0;
+-
+- if (!flush(netOutBuffer)) {
+- //we haven't emptied out the buffer yet
+- return written;
+- }
+-
+- /*
+- * The data buffer is empty, we can reuse the entire buffer.
+- */
+- netOutBuffer.clear();
+-
+- SSLEngineResult result = sslEngine.wrap(src, netOutBuffer);
+- written = result.bytesConsumed();
+- netOutBuffer.flip();
+-
+- if (result.getStatus() == Status.OK) {
+- if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) tasks();
+- } else {
+- throw new IOException("Unable to wrap data, invalid engine state:
" +result.getStatus());
+- }
+-
+- //force a flush
+- flush(netOutBuffer);
+- return written;
+- }
+- }
+-
+- /**
+- * Callback interface to be able to expand buffers
+- * when buffer overflow exceptions happen
+- */
+- public static interface ApplicationBufferHandler {
+- public ByteBuffer expand(ByteBuffer buffer, int remaining);
+- public ByteBuffer getReadBuffer();
+- public ByteBuffer getWriteBuffer();
+- }
+-
+- public ApplicationBufferHandler getBufHandler() {
+- return bufHandler;
+- }
+-
+- public boolean isInitHandshakeComplete() {
+- return initHandshakeComplete;
+- }
+-
+- public boolean isClosing() {
+- return closing;
+- }
+-
+- public SSLEngine getSslEngine() {
+- return sslEngine;
+- }
+-
+- public ByteBuffer getEmptyBuf() {
+- return emptyBuf;
+- }
+-
+- public void setBufHandler(ApplicationBufferHandler bufHandler) {
+- this.bufHandler = bufHandler;
+- }
+-
+- public SocketChannel getIOChannel() {
+- return sc;
+- }
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/tomcat/util/net/NioEndpoint.java
+===================================================================
+--- java/org/apache/tomcat/util/net/NioEndpoint.java (revision 590752)
++++ java/org/apache/tomcat/util/net/NioEndpoint.java (working copy)
+@@ -1,2197 +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.File;
+-import java.io.FileInputStream;
+-import java.io.IOException;
+-import java.net.InetAddress;
+-import java.net.InetSocketAddress;
+-import java.net.Socket;
+-import java.nio.ByteBuffer;
+-import java.nio.channels.CancelledKeyException;
+-import java.nio.channels.FileChannel;
+-import java.nio.channels.SelectionKey;
+-import java.nio.channels.Selector;
+-import java.nio.channels.ServerSocketChannel;
+-import java.nio.channels.SocketChannel;
+-import java.security.KeyStore;
+-import java.util.Collection;
+-import java.util.Iterator;
+-import java.util.Set;
+-import java.util.StringTokenizer;
+-import java.util.concurrent.ConcurrentLinkedQueue;
+-import java.util.concurrent.CountDownLatch;
+-import java.util.concurrent.Executor;
+-import java.util.concurrent.LinkedBlockingQueue;
+-import java.util.concurrent.ThreadFactory;
+-import java.util.concurrent.ThreadPoolExecutor;
+-import java.util.concurrent.TimeUnit;
+-import java.util.concurrent.atomic.AtomicInteger;
+-import java.util.concurrent.atomic.AtomicLong;
+-import javax.net.ssl.KeyManagerFactory;
+-import javax.net.ssl.SSLContext;
+-import javax.net.ssl.SSLEngine;
+-import javax.net.ssl.TrustManagerFactory;
+-
+-import org.apache.juli.logging.Log;
+-import org.apache.juli.logging.LogFactory;
+-import org.apache.tomcat.util.IntrospectionUtils;
+-import org.apache.tomcat.util.net.SecureNioChannel.ApplicationBufferHandler;
+-import org.apache.tomcat.util.res.StringManager;
+-
+-/**
+- * NIO tailored thread pool, providing the following services:
+- * <ul>
+- * <li>Socket acceptor thread</li>
+- * <li>Socket poller thread</li>
+- * <li>Worker threads pool</li>
+- * </ul>
+- *
+- * When switching to Java 5, there's an opportunity to use the virtual
+- * machine's thread pool.
+- *
+- * @author Mladen Turk
+- * @author Remy Maucherat
+- * @author Filip Hanik
+- */
+-public class NioEndpoint {
+-
+-
+- // -------------------------------------------------------------- Constants
+-
+-
+- protected static Log log = LogFactory.getLog(NioEndpoint.class);
+-
+- protected static StringManager sm =
+- StringManager.getManager("org.apache.tomcat.util.net.res");
+-
+-
+- /**
+- * The Request attribute key for the cipher suite.
+- */
+- public static final String CIPHER_SUITE_KEY =
"javax.servlet.request.cipher_suite";
+-
+- /**
+- * The Request attribute key for the key size.
+- */
+- public static final String KEY_SIZE_KEY =
"javax.servlet.request.key_size";
+-
+- /**
+- * The Request attribute key for the client certificate chain.
+- */
+- public static final String CERTIFICATE_KEY =
"javax.servlet.request.X509Certificate";
+-
+- /**
+- * The Request attribute key for the session id.
+- * This one is a Tomcat extension to the Servlet spec.
+- */
+- public static final String SESSION_ID_KEY =
"javax.servlet.request.ssl_session";
+-
+- public static final int OP_REGISTER = -1; //register interest op
+-
+- // ----------------------------------------------------------------- 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;
+-
+- protected NioSelectorPool selectorPool = new NioSelectorPool();
+-
+- /**
+- * Server socket "pointer".
+- */
+- protected ServerSocketChannel serverSock = null;
+-
+- /**
+- * use send file
+- */
+- protected boolean useSendfile = true;
+-
+- /**
+- * The size of the OOM parachute.
+- */
+- protected int oomParachute = 1024*1024;
+- /**
+- * The oom parachute, when an OOM error happens,
+- * will release the data, giving the JVM instantly
+- * a chunk of data to be able to recover with.
+- */
+- protected byte[] oomParachuteData = null;
+-
+- /**
+- * Make sure this string has already been allocated
+- */
+- protected static final String oomParachuteMsg =
+- "SEVERE:Memory usage is low, parachute is non existent, your system may
start failing.";
+-
+- /**
+- * Keep track of OOM warning messages.
+- */
+- long lastParachuteCheck = System.currentTimeMillis();
+-
+-
+-
+-
+-
+- /**
+- * Cache for SocketProcessor objects
+- */
+- protected ConcurrentLinkedQueue<SocketProcessor> processorCache = new
ConcurrentLinkedQueue<SocketProcessor>() {
+- protected AtomicInteger size = new AtomicInteger(0);
+- public boolean offer(SocketProcessor sc) {
+- sc.reset(null,null);
+- boolean offer =
socketProperties.getProcessorCache()==-1?true:size.get()<socketProperties.getProcessorCache();
+- //avoid over growing our cache or add after we have stopped
+- if ( running && (!paused) && (offer) ) {
+- boolean result = super.offer(sc);
+- if ( result ) {
+- size.incrementAndGet();
+- }
+- return result;
+- }
+- else return false;
+- }
+-
+- public SocketProcessor poll() {
+- SocketProcessor result = super.poll();
+- if ( result != null ) {
+- size.decrementAndGet();
+- }
+- return result;
+- }
+-
+- public void clear() {
+- super.clear();
+- size.set(0);
+- }
+- };
+-
+-
+- /**
+- * Cache for key attachment objects
+- */
+- protected ConcurrentLinkedQueue<KeyAttachment> keyCache = new
ConcurrentLinkedQueue<KeyAttachment>() {
+- protected AtomicInteger size = new AtomicInteger(0);
+- public boolean offer(KeyAttachment ka) {
+- ka.reset();
+- boolean offer =
socketProperties.getKeyCache()==-1?true:size.get()<socketProperties.getKeyCache();
+- //avoid over growing our cache or add after we have stopped
+- if ( running && (!paused) && (offer) ) {
+- boolean result = super.offer(ka);
+- if ( result ) {
+- size.incrementAndGet();
+- }
+- return result;
+- }
+- else return false;
+- }
+-
+- public KeyAttachment poll() {
+- KeyAttachment result = super.poll();
+- if ( result != null ) {
+- size.decrementAndGet();
+- }
+- return result;
+- }
+-
+- public void clear() {
+- super.clear();
+- size.set(0);
+- }
+- };
+-
+-
+- /**
+- * Cache for poller events
+- */
+- protected ConcurrentLinkedQueue<PollerEvent> eventCache = new
ConcurrentLinkedQueue<PollerEvent>() {
+- protected AtomicInteger size = new AtomicInteger(0);
+- public boolean offer(PollerEvent pe) {
+- pe.reset();
+- boolean offer =
socketProperties.getEventCache()==-1?true:size.get()<socketProperties.getEventCache();
+- //avoid over growing our cache or add after we have stopped
+- if ( running && (!paused) && (offer) ) {
+- boolean result = super.offer(pe);
+- if ( result ) {
+- size.incrementAndGet();
+- }
+- return result;
+- }
+- else return false;
+- }
+-
+- public PollerEvent poll() {
+- PollerEvent result = super.poll();
+- if ( result != null ) {
+- size.decrementAndGet();
+- }
+- return result;
+- }
+-
+- public void clear() {
+- super.clear();
+- size.set(0);
+- }
+- };
+-
+-
+- /**
+- * Bytebuffer cache, each channel holds a set of buffers (two, except for SSL holds
four)
+- */
+- protected ConcurrentLinkedQueue<NioChannel> nioChannels = new
ConcurrentLinkedQueue<NioChannel>() {
+- protected AtomicInteger size = new AtomicInteger(0);
+- protected AtomicInteger bytes = new AtomicInteger(0);
+- public boolean offer(NioChannel socket) {
+- boolean offer =
socketProperties.getBufferPool()==-1?true:size.get()<socketProperties.getBufferPool();
+- offer = offer &&
(socketProperties.getBufferPoolSize()==-1?true:(bytes.get()+socket.getBufferSize())<socketProperties.getBufferPoolSize());
+- //avoid over growing our cache or add after we have stopped
+- if ( running && (!paused) && (offer) ) {
+- boolean result = super.offer(socket);
+- if ( result ) {
+- size.incrementAndGet();
+- bytes.addAndGet(socket.getBufferSize());
+- }
+- return result;
+- }
+- else return false;
+- }
+-
+- public NioChannel poll() {
+- NioChannel result = super.poll();
+- if ( result != null ) {
+- size.decrementAndGet();
+- bytes.addAndGet(-result.getBufferSize());
+- }
+- return result;
+- }
+-
+- public void clear() {
+- super.clear();
+- size.set(0);
+- bytes.set(0);
+- }
+- };
+-
+-
+-
+- // ------------------------------------------------------------- Properties
+-
+-
+- /**
+- * External Executor based thread pool.
+- */
+- protected Executor executor = null;
+- public void setExecutor(Executor executor) { this.executor = executor; }
+- public Executor getExecutor() { return executor; }
+-
+- protected boolean useExecutor = true;
+- public void setUseExecutor(boolean useexec) { useExecutor = useexec;}
+- public boolean getUseExecutor() { return useExecutor || (executor!=null);}
+-
+- /**
+- * Maximum amount of worker threads.
+- */
+- protected int maxThreads = 400;
+- public void setMaxThreads(int maxThreads) { this.maxThreads = maxThreads; }
+- public int getMaxThreads() { return maxThreads; }
+-
+-
+- /**
+- * Priority of the worker threads.
+- */
+- protected int threadPriority = Thread.NORM_PRIORITY;
+- public void setThreadPriority(int threadPriority) { this.threadPriority =
threadPriority; }
+- public int getThreadPriority() { return threadPriority; }
+-
+- /**
+- * Priority of the acceptor threads.
+- */
+- protected int acceptorThreadPriority = Thread.NORM_PRIORITY;
+- public void setAcceptorThreadPriority(int acceptorThreadPriority) {
this.acceptorThreadPriority = acceptorThreadPriority; }
+- public int getAcceptorThreadPriority() { return acceptorThreadPriority; }
+-
+- /**
+- * Priority of the poller threads.
+- */
+- protected int pollerThreadPriority = Thread.NORM_PRIORITY;
+- public void setPollerThreadPriority(int pollerThreadPriority) {
this.pollerThreadPriority = pollerThreadPriority; }
+- public int getPollerThreadPriority() { return pollerThreadPriority; }
+-
+- /**
+- * 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; }
+-
+- protected SocketProperties socketProperties = new SocketProperties();
+-
+- /**
+- * Socket TCP no delay.
+- */
+- public boolean getTcpNoDelay() { return socketProperties.getTcpNoDelay();}
+- public void setTcpNoDelay(boolean tcpNoDelay) {
socketProperties.setTcpNoDelay(tcpNoDelay); }
+-
+-
+- /**
+- * Socket linger.
+- */
+- public int getSoLinger() { return socketProperties.getSoLingerTime(); }
+- public void setSoLinger(int soLinger) {
+- socketProperties.setSoLingerTime(soLinger);
+- socketProperties.setSoLingerOn(soLinger>=0);
+- }
+-
+-
+- /**
+- * Socket timeout.
+- */
+- public int getSoTimeout() { return socketProperties.getSoTimeout(); }
+- public void setSoTimeout(int soTimeout) { socketProperties.setSoTimeout(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; }
+-
+-
+-
+- /**
+- * Allow comet request handling.
+- */
+- protected boolean useComet = true;
+- public void setUseComet(boolean useComet) { this.useComet = useComet; }
+- public boolean getUseComet() { return useComet; }
+-
+-
+- /**
+- * Acceptor thread count.
+- */
+- protected int acceptorThreadCount = 1;
+- public void setAcceptorThreadCount(int acceptorThreadCount) {
this.acceptorThreadCount = acceptorThreadCount; }
+- public int getAcceptorThreadCount() { return acceptorThreadCount; }
+-
+-
+-
+- /**
+- * Poller thread count.
+- */
+- protected int pollerThreadCount = 1;
+- public void setPollerThreadCount(int pollerThreadCount) { this.pollerThreadCount =
pollerThreadCount; }
+- public int getPollerThreadCount() { return pollerThreadCount; }
+-
+- protected long selectorTimeout = 1000;
+- public void setSelectorTimeout(long timeout){ this.selectorTimeout = timeout;}
+- public long getSelectorTimeout(){ return this.selectorTimeout; }
+- /**
+- * The socket poller.
+- */
+- protected Poller[] pollers = null;
+- protected int pollerRoundRobin = 0;
+- public Poller getPoller0() {
+- pollerRoundRobin = (pollerRoundRobin + 1) % pollers.length;
+- Poller poller = pollers[pollerRoundRobin];
+- return poller;
+- }
+-
+-
+- /**
+- * The socket poller used for Comet support.
+- */
+- public Poller getCometPoller0() {
+- Poller poller = getPoller0();
+- return poller;
+- }
+-
+-
+- /**
+- * Dummy maxSpareThreads property.
+- */
+- public int getMaxSpareThreads() { return Math.min(getMaxThreads(),5); }
+-
+-
+- /**
+- * Dummy minSpareThreads property.
+- */
+- public int getMinSpareThreads() { return Math.min(getMaxThreads(),5); }
+-
+- /**
+- * Generic properties, introspected
+- */
+- public boolean setProperty(String name, String value) {
+- final String selectorPoolName = "selectorPool.";
+- final String socketName = "socket.";
+- try {
+- if (name.startsWith(selectorPoolName)) {
+- return IntrospectionUtils.setProperty(selectorPool,
name.substring(selectorPoolName.length()), value);
+- } else if (name.startsWith(socketName)) {
+- return IntrospectionUtils.setProperty(socketProperties,
name.substring(socketName.length()), value);
+- } else {
+- return IntrospectionUtils.setProperty(this,name,value);
+- }
+- }catch ( Exception x ) {
+- log.error("Unable to set attribute \""+name+"\" to
\""+value+"\"",x);
+- return false;
+- }
+- }
+-
+-
+- public String adjustRelativePath(String path, String relativeTo) {
+- File f = new File(path);
+- if ( !f.isAbsolute()) {
+- path = relativeTo + File.separator + path;
+- f = new File(path);
+- }
+- if (!f.exists()) {
+- log.warn("configured file:["+path+"] does not exist.");
+- }
+- return path;
+- }
+-
+- public String defaultIfNull(String val, String defaultValue) {
+- if (val==null) return defaultValue;
+- else return val;
+- }
+- // -------------------- SSL related properties --------------------
+- protected String truststoreFile =
System.getProperty("javax.net.ssl.trustStore");
+- public void setTruststoreFile(String s) {
+- s = adjustRelativePath(s,System.getProperty("catalina.base"));
+- this.truststoreFile = s;
+- }
+- public String getTruststoreFile() {return truststoreFile;}
+- protected String truststorePass =
System.getProperty("javax.net.ssl.trustStorePassword");
+- public void setTruststorePass(String truststorePass) {this.truststorePass =
truststorePass;}
+- public String getTruststorePass() {return truststorePass;}
+- protected String truststoreType =
System.getProperty("javax.net.ssl.trustStoreType");
+- public void setTruststoreType(String truststoreType) {this.truststoreType =
truststoreType;}
+- public String getTruststoreType() {return truststoreType;}
+-
+- protected String keystoreFile =
System.getProperty("user.home")+"/.keystore";
+- public String getKeystoreFile() { return keystoreFile;}
+- public void setKeystoreFile(String s ) {
+- s = adjustRelativePath(s,System.getProperty("catalina.base"));
+- this.keystoreFile = s;
+- }
+-
+- protected String algorithm = "SunX509";
+- public String getAlgorithm() { return algorithm;}
+- public void setAlgorithm(String s ) { this.algorithm = s;}
+-
+- protected String clientAuth = "false";
+- public String getClientAuth() { return clientAuth;}
+- public void setClientAuth(String s ) { this.clientAuth = s;}
+-
+- protected String keystorePass = "changeit";
+- public String getKeystorePass() { return keystorePass;}
+- public void setKeystorePass(String s ) { this.keystorePass = s;}
+-
+- protected String keystoreType = "JKS";
+- public String getKeystoreType() { return keystoreType;}
+- public void setKeystoreType(String s ) { this.keystoreType = s;}
+-
+- protected String sslProtocol = "TLS";
+-
+- public String getSslProtocol() { return sslProtocol;}
+- public void setSslProtocol(String s) { sslProtocol = s;}
+-
+- protected String sslEnabledProtocols=null; //"TLSv1,SSLv3,SSLv2Hello"
+- protected String[] sslEnabledProtocolsarr = new String[0];
+- public void setSslEnabledProtocols(String s) {
+- this.sslEnabledProtocols = s;
+- StringTokenizer t = new StringTokenizer(s,",");
+- sslEnabledProtocolsarr = new String[t.countTokens()];
+- for (int i=0; i<sslEnabledProtocolsarr.length; i++ )
sslEnabledProtocolsarr[i] = t.nextToken();
+- }
+-
+-
+- protected String ciphers = null;
+- protected String[] ciphersarr = new String[0];
+- public String getCiphers() { return ciphers;}
+- public void setCiphers(String s) {
+- ciphers = s;
+- if ( s == null ) ciphersarr = new String[0];
+- else {
+- StringTokenizer t = new StringTokenizer(s,",");
+- ciphersarr = new String[t.countTokens()];
+- for (int i=0; i<ciphersarr.length; i++ ) ciphersarr[i] = t.nextToken();
+- }
+- }
+-
+- /**
+- * SSL engine.
+- */
+- protected boolean SSLEnabled = false;
+- public boolean isSSLEnabled() { return SSLEnabled;}
+- public void setSSLEnabled(boolean SSLEnabled) {this.SSLEnabled = SSLEnabled;}
+-
+- protected boolean secure = false;
+- public boolean getSecure() { return secure;}
+- public void setSecure(boolean b) { secure = b;}
+-
+- public void setSelectorPool(NioSelectorPool selectorPool) {
+- this.selectorPool = selectorPool;
+- }
+-
+- public void setSocketProperties(SocketProperties socketProperties) {
+- this.socketProperties = socketProperties;
+- }
+-
+- public void setUseSendfile(boolean useSendfile) {
+-
+- this.useSendfile = useSendfile;
+- }
+-
+- public void setOomParachute(int oomParachute) {
+- this.oomParachute = oomParachute;
+- }
+-
+- public void setOomParachuteData(byte[] oomParachuteData) {
+- this.oomParachuteData = oomParachuteData;
+- }
+-
+- protected SSLContext sslContext = null;
+- public SSLContext getSSLContext() { return sslContext;}
+- public void setSSLContext(SSLContext c) { sslContext = c;}
+-
+- // --------------------------------------------------------- OOM Parachute Methods
+-
+- protected void checkParachute() {
+- boolean para = reclaimParachute(false);
+- if (!para && (System.currentTimeMillis()-lastParachuteCheck)>10000)
{
+- try {
+- log.fatal(oomParachuteMsg);
+- }catch (Throwable t) {
+- System.err.println(oomParachuteMsg);
+- }
+- lastParachuteCheck = System.currentTimeMillis();
+- }
+- }
+-
+- protected boolean reclaimParachute(boolean force) {
+- if ( oomParachuteData != null ) return true;
+- if ( oomParachute > 0 && ( force ||
(Runtime.getRuntime().freeMemory() > (oomParachute*2))) )
+- oomParachuteData = new byte[oomParachute];
+- return oomParachuteData != null;
+- }
+-
+- protected void releaseCaches() {
+- this.keyCache.clear();
+- this.nioChannels.clear();
+- this.processorCache.clear();
+- if ( handler != null ) handler.releaseCaches();
+-
+- }
+-
+- // --------------------------------------------------------- Public Methods
+- /**
+- * Number of keepalive sockets.
+- */
+- public int getKeepAliveCount() {
+- if (pollers == null) {
+- return 0;
+- } else {
+- int keepAliveCount = 0;
+- for (int i = 0; i < pollers.length; i++) {
+- keepAliveCount += pollers[i].getKeepAliveCount();
+- }
+- return keepAliveCount;
+- }
+- }
+-
+-
+-
+- /**
+- * 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;
+- }
+-
+-
+- /**
+- * Return the state of the endpoint.
+- *
+- * @return true if the endpoint is running, false otherwise
+- */
+- public boolean isRunning() {
+- return running;
+- }
+-
+-
+- /**
+- * Return the state of the endpoint.
+- *
+- * @return true if the endpoint is paused, false otherwise
+- */
+- public boolean isPaused() {
+- return paused;
+- }
+-
+-
+- // ----------------------------------------------- Public Lifecycle Methods
+-
+-
+- /**
+- * Initialize the endpoint.
+- */
+- public void init()
+- throws Exception {
+-
+- if (initialized)
+- return;
+-
+- serverSock = ServerSocketChannel.open();
+-
serverSock.socket().setPerformancePreferences(socketProperties.getPerformanceConnectionTime(),
+-
socketProperties.getPerformanceLatency(),
+-
socketProperties.getPerformanceBandwidth());
+- InetSocketAddress addr = (address!=null?new InetSocketAddress(address,port):new
InetSocketAddress(port));
+- serverSock.socket().bind(addr,backlog);
+- serverSock.configureBlocking(true); //mimic APR behavior
+-
+- // Initialize thread count defaults for acceptor, poller
+- if (acceptorThreadCount == 0) {
+- // FIXME: Doesn't seem to work that well with multiple accept threads
+- acceptorThreadCount = 1;
+- }
+- if (pollerThreadCount <= 0) {
+- //minimum one poller thread
+- pollerThreadCount = 1;
+- }
+-
+- // Initialize SSL if needed
+- if (isSSLEnabled()) {
+- // Initialize SSL
+- char[] passphrase = getKeystorePass().toCharArray();
+-
+- KeyStore ks = KeyStore.getInstance(getKeystoreType());
+- ks.load(new FileInputStream(getKeystoreFile()), passphrase);
+-
+- KeyManagerFactory kmf = KeyManagerFactory.getInstance(getAlgorithm());
+- kmf.init(ks, passphrase);
+-
+- char[] tpassphrase =
(getTruststorePass()!=null)?getTruststorePass().toCharArray():passphrase;
+- String ttype =
(getTruststoreType()!=null)?getTruststoreType():getKeystoreType();
+-
+- KeyStore ts = null;
+- if (getTruststoreFile()==null) {
+- ts = KeyStore.getInstance(getKeystoreType());
+- ts.load(new FileInputStream(getKeystoreFile()), passphrase);
+- }else {
+- ts = KeyStore.getInstance(ttype);
+- ts.load(new FileInputStream(getTruststoreFile()), tpassphrase);
+- }
+-
+- TrustManagerFactory tmf = TrustManagerFactory.getInstance(getAlgorithm());
+- tmf.init(ts);
+-
+- sslContext = SSLContext.getInstance(getSslProtocol());
+- sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+-
+- }
+-
+- if (oomParachute>0) reclaimParachute(true);
+-
+- initialized = true;
+-
+- }
+-
+-
+- /**
+- * Start the NIO endpoint, creating acceptor, poller threads.
+- */
+- public void start()
+- throws Exception {
+- // Initialize socket if not done before
+- if (!initialized) {
+- init();
+- }
+- if (!running) {
+- running = true;
+- paused = false;
+-
+- // Create worker collection
+- if (getUseExecutor()) {
+- if ( executor == null ) {
+- TaskQueue taskqueue = new TaskQueue();
+- TaskThreadFactory tf = new TaskThreadFactory(getName() +
"-exec-");
+- executor = new ThreadPoolExecutor(getMinSpareThreads(),
getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
+- taskqueue.setParent( (ThreadPoolExecutor) executor);
+- }
+- } else if ( executor == null ) {//avoid two thread pools being created
+- workers = new WorkerStack(maxThreads);
+- }
+-
+- // 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();
+- }
+-
+- // Start poller threads
+- pollers = new Poller[pollerThreadCount];
+- for (int i = 0; i < pollerThreadCount; i++) {
+- pollers[i] = new Poller();
+- pollers[i].init();
+- Thread pollerThread = new Thread(pollers[i], getName() +
"-Poller-" + i);
+- pollerThread.setPriority(threadPriority);
+- pollerThread.setDaemon(true);
+- pollerThread.start();
+- }
+- }
+- }
+-
+-
+- /**
+- * 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 sockets
+- * again.
+- */
+- public void resume() {
+- if (running) {
+- paused = false;
+- }
+- }
+-
+-
+- /**
+- * Stop the endpoint. This will cause all processing threads to stop.
+- */
+- public void stop() {
+- if (running) {
+- running = false;
+- unlockAccept();
+- for (int i = 0; i < pollers.length; i++) {
+- pollers[i].destroy();
+- }
+- pollers = null;
+- }
+- eventCache.clear();
+- keyCache.clear();
+- nioChannels.clear();
+- processorCache.clear();
+- if ( executor!=null ) {
+- if ( executor instanceof ThreadPoolExecutor ) {
+- //this is our internal one, so we need to shut it down
+- ThreadPoolExecutor tpe = (ThreadPoolExecutor) executor;
+- tpe.shutdown();
+- TaskQueue queue = (TaskQueue) tpe.getQueue();
+- queue.setParent(null);
+- }
+- executor = null;
+- }
+- }
+-
+-
+- /**
+- * Deallocate NIO memory pools, and close server socket.
+- */
+- public void destroy() throws Exception {
+- if (running) {
+- stop();
+- }
+- // Close server socket
+- serverSock.socket().close();
+- serverSock.close();
+- serverSock = null;
+- sslContext = null;
+- initialized = false;
+- releaseCaches();
+- }
+-
+-
+- // ------------------------------------------------------ Protected Methods
+-
+-
+- /**
+- * Get a sequence number used for thread naming.
+- */
+- protected int getSequence() {
+- return sequence++;
+- }
+-
+- public int getWriteBufSize() {
+- return socketProperties.getTxBufSize();
+- }
+-
+- public int getReadBufSize() {
+- return socketProperties.getRxBufSize();
+- }
+-
+- public NioSelectorPool getSelectorPool() {
+- return selectorPool;
+- }
+-
+- public SocketProperties getSocketProperties() {
+- return socketProperties;
+- }
+-
+- public boolean getUseSendfile() {
+- //send file doesn't work with SSL
+- return useSendfile && (!isSSLEnabled());
+- }
+-
+- public int getOomParachute() {
+- return oomParachute;
+- }
+-
+- public byte[] getOomParachuteData() {
+- return oomParachuteData;
+- }
+-
+- /**
+- * 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("127.0.0.1", 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);
+- }
+- } 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
+- }
+- }
+- }
+- }
+-
+-
+- /**
+- * Process the specified connection.
+- */
+- protected boolean setSocketOptions(SocketChannel socket) {
+- // Process the connection
+- try {
+- //disable blocking, APR style, we are gonna be polling it
+- socket.configureBlocking(false);
+- Socket sock = socket.socket();
+- socketProperties.setProperties(sock);
+-
+- NioChannel channel = nioChannels.poll();
+- if ( channel == null ) {
+- // SSL setup
+- if (sslContext != null) {
+- SSLEngine engine = createSSLEngine();
+- int appbufsize = engine.getSession().getApplicationBufferSize();
+- NioBufferHandler bufhandler = new
NioBufferHandler(Math.max(appbufsize,socketProperties.getAppReadBufSize()),
+-
Math.max(appbufsize,socketProperties.getAppWriteBufSize()),
+-
socketProperties.getDirectBuffer());
+- channel = new SecureNioChannel(socket, engine, bufhandler,
selectorPool);
+- } else {
+- // normal tcp setup
+- NioBufferHandler bufhandler = new
NioBufferHandler(socketProperties.getAppReadBufSize(),
+-
socketProperties.getAppWriteBufSize(),
+-
socketProperties.getDirectBuffer());
+-
+- channel = new NioChannel(socket, bufhandler);
+- }
+- } else {
+- channel.setIOChannel(socket);
+- if ( channel instanceof SecureNioChannel ) {
+- SSLEngine engine = createSSLEngine();
+- ((SecureNioChannel)channel).reset(engine);
+- } else {
+- channel.reset();
+- }
+- }
+- getPoller0().register(channel);
+- } catch (Throwable t) {
+- try {
+- log.error("",t);
+- }catch ( Throwable tt){}
+- // Tell to close the socket
+- return false;
+- }
+- return true;
+- }
+-
+- protected SSLEngine createSSLEngine() {
+- SSLEngine engine = sslContext.createSSLEngine();
+- if ("false".equals(getClientAuth())) {
+- engine.setNeedClientAuth(false);
+- engine.setWantClientAuth(false);
+- } else if ("true".equals(getClientAuth()) ||
"yes".equals(getClientAuth())){
+- engine.setNeedClientAuth(true);
+- } else if ("want".equals(getClientAuth())) {
+- engine.setWantClientAuth(true);
+- }
+- engine.setUseClientMode(false);
+- if ( ciphersarr.length > 0 ) engine.setEnabledCipherSuites(ciphersarr);
+- if ( sslEnabledProtocolsarr.length > 0 )
engine.setEnabledProtocols(sslEnabledProtocolsarr);
+-
+- return engine;
+- }
+-
+-
+- /**
+- * Returns true if a worker thread is available for processing.
+- * @return boolean
+- */
+- protected boolean isWorkerAvailable() {
+- if ( executor != null ) {
+- return true;
+- } else {
+- if (workers.size() > 0) {
+- return true;
+- }
+- if ( (maxThreads > 0) && (curThreads < maxThreads)) {
+- return true;
+- } else {
+- if (maxThreads < 0) {
+- return true;
+- } else {
+- return false;
+- }
+- }
+- }
+- }
+- /**
+- * 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++;
+- 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();
+- while (workerThread == null) {
+- try {
+- synchronized (workers) {
+- workerThread = createWorkerThread();
+- if ( workerThread == null ) workers.wait();
+- }
+- } catch (InterruptedException e) {
+- // Ignore
+- }
+- if ( workerThread == null ) 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(NioChannel socket) {
+- return processSocket(socket,null);
+- }
+-
+-
+- /**
+- * Process given socket for an event.
+- */
+- protected boolean processSocket(NioChannel socket, SocketStatus status) {
+- return processSocket(socket,status,true);
+- }
+-
+- protected boolean processSocket(NioChannel socket, SocketStatus status, boolean
dispatch) {
+- try {
+- if (executor == null) {
+- getWorkerThread().assign(socket, status);
+- } else {
+- SocketProcessor sc = processorCache.poll();
+- if ( sc == null ) sc = new SocketProcessor(socket,status);
+- else sc.reset(socket,status);
+- if ( dispatch ) executor.execute(sc);
+- else sc.run();
+- }
+- } 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;
+- }
+-
+-
+- // --------------------------------------------------- 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
+- }
+- }
+- try {
+- // Accept the next incoming connection from the server socket
+- SocketChannel socket = serverSock.accept();
+- // Hand this socket off to an appropriate processor
+- //TODO FIXME - this is currently a blocking call, meaning we will be
blocking
+- //further accepts until there is a thread available.
+- if ( running && (!paused) && socket != null ) {
+- //processSocket(socket);
+- if (!setSocketOptions(socket)) {
+- try {
+- socket.socket().close();
+- socket.close();
+- } catch (IOException ix) {
+- if (log.isDebugEnabled())
+- log.debug("", ix);
+- }
+- }
+- }
+- }catch ( IOException x ) {
+- if ( running )
log.error(sm.getString("endpoint.accept.fail"), x);
+- } catch (OutOfMemoryError oom) {
+- try {
+- oomParachuteData = null;
+- releaseCaches();
+- log.error("", oom);
+- }catch ( Throwable oomt ) {
+- try {
+- try {
+- System.err.println(oomParachuteMsg);
+- oomt.printStackTrace();
+- }catch (Throwable letsHopeWeDontGetHere){}
+- }catch (Throwable letsHopeWeDontGetHere){}
+- }
+- } catch (Throwable t) {
+- log.error(sm.getString("endpoint.accept.fail"), t);
+- }
+- }//while
+- }//run
+- }
+-
+-
+- // ----------------------------------------------------- Poller Inner Classes
+-
+- /**
+- *
+- * PollerEvent, cacheable object for poller events to avoid GC
+- */
+- public class PollerEvent implements Runnable {
+-
+- protected NioChannel socket;
+- protected int interestOps;
+- protected KeyAttachment key;
+- public PollerEvent(NioChannel ch, KeyAttachment k, int intOps) {
+- reset(ch, k, intOps);
+- }
+-
+- public void reset(NioChannel ch, KeyAttachment k, int intOps) {
+- socket = ch;
+- interestOps = intOps;
+- key = k;
+- }
+-
+- public void reset() {
+- reset(null, null, 0);
+- }
+-
+- public void run() {
+- if ( interestOps == OP_REGISTER ) {
+- try {
+- socket.getIOChannel().register(socket.getPoller().getSelector(),
SelectionKey.OP_READ, key);
+- } catch (Exception x) {
+- log.error("", x);
+- }
+- } else {
+- final SelectionKey key =
socket.getIOChannel().keyFor(socket.getPoller().getSelector());
+- try {
+- boolean cancel = false;
+- if (key != null) {
+- final KeyAttachment att = (KeyAttachment) key.attachment();
+- if ( att!=null ) {
+- att.access();//to prevent timeout
+- //we are registering the key to start with, reset the
fairness counter.
+- att.setFairness(0);
+- att.interestOps(interestOps);
+- key.interestOps(interestOps);
+- } else {
+- cancel = true;
+- }
+- } else {
+- cancel = true;
+- }
+- if ( cancel )
getPoller0().cancelledKey(key,SocketStatus.ERROR,false);
+- }catch (CancelledKeyException ckx) {
+- try {
+- getPoller0().cancelledKey(key,SocketStatus.DISCONNECT,true);
+- }catch (Exception ignore) {}
+- }
+- }//end if
+- }//run
+-
+- public String toString() {
+- return
super.toString()+"[intOps="+this.interestOps+"]";
+- }
+- }
+- /**
+- * Poller class.
+- */
+- public class Poller implements Runnable {
+-
+- protected Selector selector;
+- protected ConcurrentLinkedQueue<Runnable> events = new
ConcurrentLinkedQueue<Runnable>();
+-
+- protected boolean close = false;
+- protected long nextExpiration = 0;//optimize expiration handling
+-
+- protected int keepAliveCount = 0;
+- public int getKeepAliveCount() { return keepAliveCount; }
+-
+- protected AtomicLong wakeupCounter = new AtomicLong(0l);
+-
+- protected CountDownLatch stopLatch = new CountDownLatch(1);
+-
+-
+-
+- public Poller() throws IOException {
+- this.selector = Selector.open();
+- }
+-
+- public Selector getSelector() { return selector;}
+-
+- /**
+- * Create the poller. With some versions of APR, the maximum poller size will
+- * be 62 (reocmpiling APR is necessary to remove this limitation).
+- */
+- protected void init() {
+- keepAliveCount = 0;
+- }
+-
+- /**
+- * Destroy the poller.
+- */
+- protected void destroy() {
+- // Wait for polltime before doing anything, so that the poller threads
+- // exit, otherwise parallel descturction of sockets which are still
+- // in the poller can cause problems
+- close = true;
+- events.clear();
+- selector.wakeup();
+- try { stopLatch.await(5,TimeUnit.SECONDS); } catch (InterruptedException
ignore ) {}
+- }
+-
+- public void addEvent(Runnable event) {
+- events.offer(event);
+- if ( wakeupCounter.incrementAndGet() < 3 ) selector.wakeup();
+- }
+-
+- /**
+- * 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(final NioChannel socket) {
+- add(socket,SelectionKey.OP_READ);
+- }
+-
+- public void add(final NioChannel socket, final int interestOps) {
+- PollerEvent r = eventCache.poll();
+- if ( r==null) r = new PollerEvent(socket,null,interestOps);
+- else r.reset(socket,null,interestOps);
+- addEvent(r);
+- }
+-
+- public boolean events() {
+- boolean result = false;
+- //synchronized (events) {
+- Runnable r = null;
+- result = (events.size() > 0);
+- while ( (r = (Runnable)events.poll()) != null ) {
+- try {
+- r.run();
+- if ( r instanceof PollerEvent ) {
+- ((PollerEvent)r).reset();
+- eventCache.offer((PollerEvent)r);
+- }
+- } catch ( Throwable x ) {
+- log.error("",x);
+- }
+- }
+- //events.clear();
+- //}
+- return result;
+- }
+-
+- public void register(final NioChannel socket)
+- {
+- socket.setPoller(this);
+- KeyAttachment key = keyCache.poll();
+- final KeyAttachment ka = key!=null?key:new KeyAttachment();
+- ka.reset(this,socket);
+- PollerEvent r = eventCache.poll();
+- ka.interestOps(SelectionKey.OP_READ);//this is what OP_REGISTER turns into.
+- if ( r==null) r = new PollerEvent(socket,ka,OP_REGISTER);
+- else r.reset(socket,ka,OP_REGISTER);
+- addEvent(r);
+- }
+- public void cancelledKey(SelectionKey key, SocketStatus status, boolean
dispatch) {
+- try {
+- if ( key == null ) return;//nothing to do
+- KeyAttachment ka = (KeyAttachment) key.attachment();
+- if (ka != null && ka.getComet() && status != null) {
+- //the comet event takes care of clean up
+- //processSocket(ka.getChannel(), status, dispatch);
+- ka.setComet(false);//to avoid a loop
+- processSocket(ka.getChannel(), status, false);//don't dispatch
if the lines below are cancelling the key
+- if (status == SocketStatus.TIMEOUT ) return; // don't close on
comet timeout
+- }
+- if (key.isValid()) key.cancel();
+- if (key.channel().isOpen()) try {key.channel().close();}catch (Exception
ignore){}
+- try {ka.channel.close(true);}catch (Exception ignore){}
+- key.attach(null);
+- } catch (Throwable e) {
+- if ( log.isDebugEnabled() ) log.error("",e);
+- // Ignore
+- }
+- }
+- /**
+- * 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) {
+- try {
+- // Loop if endpoint is paused
+- while (paused && (!close) ) {
+- try {
+- Thread.sleep(500);
+- } catch (InterruptedException e) {
+- // Ignore
+- }
+- }
+- boolean hasEvents = false;
+-
+- hasEvents = (hasEvents | events());
+- // Time to terminate?
+- if (close) {
+- timeout(0, false);
+- stopLatch.countDown();
+- return;
+- }
+- int keyCount = 0;
+- try {
+- if ( !close ) {
+- keyCount = selector.select(selectorTimeout);
+- wakeupCounter.set(0);
+- }
+- if (close) {
+- timeout(0, false);
+- stopLatch.countDown();
+- selector.close();
+- return;
+- }
+- } catch ( NullPointerException x ) {
+- //sun bug 5076772 on windows JDK 1.5
+- if ( wakeupCounter == null || selector == null ) throw x;
+- continue;
+- } catch ( CancelledKeyException x ) {
+- //sun bug 5076772 on windows JDK 1.5
+- if ( wakeupCounter == null || selector == null ) throw x;
+- continue;
+- } catch (Throwable x) {
+- log.error("",x);
+- continue;
+- }
+- //either we timed out or we woke up, process events first
+- if ( keyCount == 0 ) hasEvents = (hasEvents | events());
+-
+- Iterator iterator = keyCount > 0 ?
selector.selectedKeys().iterator() : null;
+- // Walk through the collection of ready keys and dispatch
+- // any active event.
+- while (iterator != null && iterator.hasNext()) {
+- SelectionKey sk = (SelectionKey) iterator.next();
+- KeyAttachment attachment = (KeyAttachment)sk.attachment();
+- iterator.remove();
+- processKey(sk, attachment);
+- }//while
+-
+- //process timeouts
+- timeout(keyCount,hasEvents);
+- if ( oomParachute > 0 && oomParachuteData == null )
checkParachute();
+- } catch (OutOfMemoryError oom) {
+- try {
+- oomParachuteData = null;
+- releaseCaches();
+- log.error("", oom);
+- }catch ( Throwable oomt ) {
+- try {
+- System.err.println(oomParachuteMsg);
+- oomt.printStackTrace();
+- }catch (Throwable letsHopeWeDontGetHere){}
+- }
+- }
+- }//while
+- synchronized (this) {
+- this.notifyAll();
+- }
+- stopLatch.countDown();
+-
+- }
+-
+- protected boolean processKey(SelectionKey sk, KeyAttachment attachment) {
+- boolean result = true;
+- try {
+- if ( close ) {
+- cancelledKey(sk, SocketStatus.STOP, false);
+- } else if ( sk.isValid() && attachment != null ) {
+- attachment.access();//make sure we don't time out valid sockets
+- sk.attach(attachment);//cant remember why this is here
+- NioChannel channel = attachment.getChannel();
+- if (sk.isReadable() || sk.isWritable() ) {
+- if ( sk.isReadable() && attachment.getReadLatch() !=
null ) {
+- unreg(sk, attachment,SelectionKey.OP_READ);
+- attachment.getReadLatch().countDown();
+- } else if ( sk.isWritable() &&
attachment.getWriteLatch() != null ) {
+- unreg(sk, attachment,SelectionKey.OP_WRITE);
+- attachment.getWriteLatch().countDown();
+- } else if ( attachment.getSendfileData() != null ) {
+- processSendfile(sk,attachment,true);
+- } else if ( attachment.getComet() ) {
+- //check if thread is available
+- if ( isWorkerAvailable() ) {
+- unreg(sk, attachment, sk.readyOps());
+- if (!processSocket(channel, SocketStatus.OPEN))
+- processSocket(channel, SocketStatus.DISCONNECT);
+- attachment.setFairness(0);
+- } else {
+- //increase the fairness counter
+- attachment.incFairness();
+- result = false;
+- }
+- } else {
+- //later on, improve latch behavior
+- if ( isWorkerAvailable() ) {
+- unreg(sk, attachment,sk.readyOps());
+- boolean close = (!processSocket(channel));
+- if (close) {
+- cancelledKey(sk,SocketStatus.DISCONNECT,false);
+- }
+- attachment.setFairness(0);
+- } else {
+- //increase the fairness counter
+- attachment.incFairness();
+- result = false;
+- }
+- }
+- }
+- } else {
+- //invalid key
+- cancelledKey(sk, SocketStatus.ERROR,false);
+- }
+- } catch ( CancelledKeyException ckx ) {
+- cancelledKey(sk, SocketStatus.ERROR,false);
+- } catch (Throwable t) {
+- log.error("",t);
+- }
+- return result;
+- }
+-
+- public boolean processSendfile(SelectionKey sk, KeyAttachment attachment,
boolean reg) {
+- try {
+- //unreg(sk,attachment);//only do this if we do process send file on a
separate thread
+- SendfileData sd = attachment.getSendfileData();
+- if ( sd.fchannel == null ) {
+- File f = new File(sd.fileName);
+- if ( !f.exists() ) {
+- cancelledKey(sk,SocketStatus.ERROR,false);
+- return false;
+- }
+- sd.fchannel = new FileInputStream(f).getChannel();
+- }
+- SocketChannel sc = attachment.getChannel().getIOChannel();
+- long written = sd.fchannel.transferTo(sd.pos,sd.length,sc);
+- if ( written > 0 ) {
+- sd.pos += written;
+- sd.length -= written;
+- }
+- if ( sd.length <= 0 ) {
+- attachment.setSendfileData(null);
+- if ( sd.keepAlive )
+- if (reg) reg(sk,attachment,SelectionKey.OP_READ);
+- else
+- cancelledKey(sk,SocketStatus.STOP,false);
+- } else if ( attachment.interestOps() == 0 && reg ) {
+- reg(sk,attachment,SelectionKey.OP_WRITE);
+- }
+- }catch ( IOException x ) {
+- if ( log.isDebugEnabled() ) log.warn("Unable to complete sendfile
request:", x);
+- cancelledKey(sk,SocketStatus.ERROR,false);
+- return false;
+- }catch ( Throwable t ) {
+- log.error("",t);
+- cancelledKey(sk, SocketStatus.ERROR, false);
+- return false;
+- }
+- return true;
+- }
+-
+- protected void unreg(SelectionKey sk, KeyAttachment attachment, int readyOps) {
+- //this is a must, so that we don't have multiple threads messing with
the socket
+- reg(sk,attachment,sk.interestOps()& (~readyOps));
+- }
+-
+- protected void reg(SelectionKey sk, KeyAttachment attachment, int intops) {
+- sk.interestOps(intops);
+- attachment.interestOps(intops);
+- }
+-
+- protected void timeout(int keyCount, boolean hasEvents) {
+- long now = System.currentTimeMillis();
+- //don't process timeouts too frequently, but if the selector simply
timed out
+- //then we can check timeouts to avoid gaps
+- if ( ((keyCount>0 || hasEvents) ||(now < nextExpiration)) &&
(!close) ) {
+- return;
+- }
+- long prevExp = nextExpiration;
+- nextExpiration = now + socketProperties.getTimeoutInterval();
+- //timeout
+- Set<SelectionKey> keys = selector.keys();
+- int keycount = 0;
+- for (Iterator<SelectionKey> iter = keys.iterator(); iter.hasNext(); )
{
+- SelectionKey key = iter.next();
+- keycount++;
+- try {
+- KeyAttachment ka = (KeyAttachment) key.attachment();
+- if ( ka == null ) {
+- cancelledKey(key, SocketStatus.ERROR,false); //we don't
support any keys without attachments
+- } else if ( ka.getError() ) {
+- cancelledKey(key, SocketStatus.ERROR,true);
+- }else if ((ka.interestOps()&SelectionKey.OP_READ) ==
SelectionKey.OP_READ) {
+- //only timeout sockets that we are waiting for a read from
+- long delta = now - ka.getLastAccess();
+- long timeout = (ka.getTimeout()==-1)?((long)
socketProperties.getSoTimeout()):(ka.getTimeout());
+- boolean isTimedout = delta > timeout;
+- if ( close ) {
+- key.interestOps(0);
+- ka.interestOps(0); //avoid duplicate stop calls
+- processKey(key,ka);
+- } else if (isTimedout) {
+- key.interestOps(0);
+- ka.interestOps(0); //avoid duplicate timeout calls
+- cancelledKey(key, SocketStatus.TIMEOUT,true);
+- } else {
+- long nextTime = now+(timeout-delta);
+- nextExpiration = (nextTime <
nextExpiration)?nextTime:nextExpiration;
+- }
+- }//end if
+- }catch ( CancelledKeyException ckx ) {
+- cancelledKey(key, SocketStatus.ERROR,false);
+- }
+- }//for
+- if ( log.isDebugEnabled() ) log.debug("timeout completed:
keycount="+keycount+"; now="+now+";
nextExpiration="+prevExp+"; "+
+- "keyCount="+keyCount+";
hasEvents="+hasEvents +"; eval="+( (now < prevExp) &&
(keyCount>0 || hasEvents) && (!close) ));
+-
+- }
+- }
+-
+-// ----------------------------------------------------- Key Attachment Class
+- public static class KeyAttachment {
+-
+- public KeyAttachment() {
+-
+- }
+- public void reset(Poller poller, NioChannel channel) {
+- this.channel = channel;
+- this.poller = poller;
+- lastAccess = System.currentTimeMillis();
+- currentAccess = false;
+- comet = false;
+- timeout = -1;
+- error = false;
+- fairness = 0;
+- lastRegistered = 0;
+- sendfileData = null;
+- if ( readLatch!=null ) try {for (int i=0;
i<(int)readLatch.getCount();i++) readLatch.countDown();}catch (Exception ignore){}
+- readLatch = null;
+- if ( writeLatch!=null ) try {for (int i=0;
i<(int)writeLatch.getCount();i++) writeLatch.countDown();}catch (Exception ignore){}
+- writeLatch = null;
+- }
+-
+- public void reset() {
+- reset(null,null);
+- }
+-
+- public Poller getPoller() { return poller;}
+- public void setPoller(Poller poller){this.poller = poller;}
+- public long getLastAccess() { return lastAccess; }
+- public void access() { access(System.currentTimeMillis()); }
+- public void access(long access) { lastAccess = access; }
+- public void setComet(boolean comet) { this.comet = comet; }
+- public boolean getComet() { return comet; }
+- public boolean getCurrentAccess() { return currentAccess; }
+- public void setCurrentAccess(boolean access) { currentAccess = access; }
+- public Object getMutex() {return mutex;}
+- public void setTimeout(long timeout) {this.timeout = timeout;}
+- public long getTimeout() {return this.timeout;}
+- public boolean getError() { return error; }
+- public void setError(boolean error) { this.error = error; }
+- public NioChannel getChannel() { return channel;}
+- public void setChannel(NioChannel channel) { this.channel = channel;}
+- protected Poller poller = null;
+- protected int interestOps = 0;
+- public int interestOps() { return interestOps;}
+- public int interestOps(int ops) { this.interestOps = ops; return ops; }
+- public CountDownLatch getReadLatch() { return readLatch; }
+- public CountDownLatch getWriteLatch() { return writeLatch; }
+- protected CountDownLatch resetLatch(CountDownLatch latch) {
+- if ( latch.getCount() == 0 ) return null;
+- else throw new IllegalStateException("Latch must be at count 0");
+- }
+- public void resetReadLatch() { readLatch = resetLatch(readLatch); }
+- public void resetWriteLatch() { writeLatch = resetLatch(writeLatch); }
+-
+- protected CountDownLatch startLatch(CountDownLatch latch, int cnt) {
+- if ( latch == null || latch.getCount() == 0 ) {
+- return new CountDownLatch(cnt);
+- }
+- else throw new IllegalStateException("Latch must be at count 0 or
null.");
+- }
+- public void startReadLatch(int cnt) { readLatch = startLatch(readLatch,cnt);}
+- public void startWriteLatch(int cnt) { writeLatch =
startLatch(writeLatch,cnt);}
+-
+-
+- protected void awaitLatch(CountDownLatch latch, long timeout, TimeUnit unit)
throws InterruptedException {
+- if ( latch == null ) throw new IllegalStateException("Latch cannot be
null");
+- latch.await(timeout,unit);
+- }
+- public void awaitReadLatch(long timeout, TimeUnit unit) throws
InterruptedException { awaitLatch(readLatch,timeout,unit);}
+- public void awaitWriteLatch(long timeout, TimeUnit unit) throws
InterruptedException { awaitLatch(writeLatch,timeout,unit);}
+-
+- public int getFairness() { return fairness; }
+- public void setFairness(int f) { fairness = f;}
+- public void incFairness() { fairness++; }
+- public long getLastRegistered() { return lastRegistered; };
+- public void setLastRegistered(long reg) { lastRegistered = reg; }
+-
+- public void setSendfileData(SendfileData sf) { this.sendfileData = sf;}
+- public SendfileData getSendfileData() { return this.sendfileData;}
+-
+- protected Object mutex = new Object();
+- protected long lastAccess = -1;
+- protected boolean currentAccess = false;
+- protected boolean comet = false;
+- protected long timeout = -1;
+- protected boolean error = false;
+- protected NioChannel channel = null;
+- protected CountDownLatch readLatch = null;
+- protected CountDownLatch writeLatch = null;
+- protected int fairness = 0;
+- protected long lastRegistered = 0;
+- protected SendfileData sendfileData = null;
+- }
+- // ----------------------------------------------------- Worker Inner Class
+-
+-
+- /**
+- * Server processor class.
+- */
+- protected class Worker implements Runnable {
+-
+-
+- protected Thread thread = null;
+- protected boolean available = false;
+- protected Object 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(Object 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;
+- status = null;
+- available = true;
+- notifyAll();
+-
+- }
+-
+-
+- protected synchronized void assign(Object 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.
+- */
+- protected synchronized Object 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
+- Object 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) {
+- NioChannel socket = null;
+- SelectionKey key = null;
+- try {
+- // Wait for the next socket to be assigned
+- Object channel = await();
+- if (channel == null)
+- continue;
+-
+- if ( channel instanceof SocketChannel) {
+- SocketChannel sc = (SocketChannel)channel;
+- if ( !setSocketOptions(sc) ) {
+- try {
+- sc.socket().close();
+- sc.close();
+- }catch ( IOException ix ) {
+- if ( log.isDebugEnabled() ) log.debug("",ix);
+- }
+- } else {
+- //now we have it registered, remove it from the cache
+-
+- }
+- } else {
+- socket = (NioChannel)channel;
+- SocketProcessor sc = processorCache.poll();
+- if ( sc == null ) sc = new SocketProcessor(socket,status);
+- else sc.reset(socket,status);
+- sc.run();
+- }
+- }catch(CancelledKeyException cx) {
+- if (socket!=null && key!=null)
socket.getPoller().cancelledKey(key,null,false);
+- } catch (OutOfMemoryError oom) {
+- try {
+- oomParachuteData = null;
+- releaseCaches();
+- log.error("", oom);
+- }catch ( Throwable oomt ) {
+- try {
+- System.err.println(oomParachuteMsg);
+- oomt.printStackTrace();
+- }catch (Throwable letsHopeWeDontGetHere){}
+- }
+- } finally {
+- //dereference socket to let GC do its job
+- socket = null;
+- // 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.setPriority(getThreadPriority());
+- thread.start();
+- }
+-
+-
+- }
+-
+- // ------------------------------------------------ Application Buffer Handler
+- public class NioBufferHandler implements ApplicationBufferHandler {
+- protected ByteBuffer readbuf = null;
+- protected ByteBuffer writebuf = null;
+-
+- public NioBufferHandler(int readsize, int writesize, boolean direct) {
+- if ( direct ) {
+- readbuf = ByteBuffer.allocateDirect(readsize);
+- writebuf = ByteBuffer.allocateDirect(writesize);
+- }else {
+- readbuf = ByteBuffer.allocate(readsize);
+- writebuf = ByteBuffer.allocate(writesize);
+- }
+- }
+-
+- public ByteBuffer expand(ByteBuffer buffer, int remaining) {return buffer;}
+- public ByteBuffer getReadBuffer() {return readbuf;}
+- public ByteBuffer getWriteBuffer() {return writebuf;}
+-
+- }
+-
+- // ------------------------------------------------ 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(NioChannel socket);
+- public SocketState event(NioChannel socket, SocketStatus status);
+- public void releaseCaches();
+- }
+-
+-
+- // ------------------------------------------------- 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);
+- }
+- }
+-
+-
+- // ---------------------------------------------- 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 NioChannel socket = null;
+- protected SocketStatus status = null;
+-
+- public SocketProcessor(NioChannel socket, SocketStatus status) {
+- reset(socket,status);
+- }
+-
+- public void reset(NioChannel socket, SocketStatus status) {
+- this.socket = socket;
+- this.status = status;
+- }
+-
+- public void run() {
+- SelectionKey key = null;
+- try {
+- key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());
+- int handshake = -1;
+-
+- try {
+- if (key!=null) handshake = socket.handshake(key.isReadable(),
key.isWritable());
+- }catch ( IOException x ) {
+- handshake = -1;
+- if ( log.isDebugEnabled() ) log.debug("Error during SSL
handshake",x);
+- }catch ( CancelledKeyException ckx ) {
+- handshake = -1;
+- }
+- if ( handshake == 0 ) {
+- // Process the request from this socket
+- boolean closed =
(status==null)?(handler.process(socket)==Handler.SocketState.CLOSED) :
+- (handler.event(socket,status)==Handler.SocketState.CLOSED);
+-
+- if (closed) {
+- // Close socket and pool
+- try {
+- KeyAttachment ka = null;
+- if (key!=null) {
+- ka = (KeyAttachment) key.attachment();
+- if (ka!=null) ka.setComet(false);
+- socket.getPoller().cancelledKey(key, SocketStatus.ERROR,
false);
+- }
+- if (socket!=null) nioChannels.offer(socket);
+- socket = null;
+- if ( ka!=null ) keyCache.offer(ka);
+- ka = null;
+- }catch ( Exception x ) {
+- log.error("",x);
+- }
+- }
+- } else if (handshake == -1 ) {
+- KeyAttachment ka = null;
+- if (key!=null) {
+- ka = (KeyAttachment) key.attachment();
+- socket.getPoller().cancelledKey(key, SocketStatus.DISCONNECT,
false);
+- }
+- if (socket!=null) nioChannels.offer(socket);
+- socket = null;
+- if ( ka!=null ) keyCache.offer(ka);
+- ka = null;
+- } else {
+- final SelectionKey fk = key;
+- final int intops = handshake;
+- final KeyAttachment ka = (KeyAttachment)fk.attachment();
+- ka.getPoller().add(socket,intops);
+- }
+- }catch(CancelledKeyException cx) {
+- socket.getPoller().cancelledKey(key,null,false);
+- } catch (OutOfMemoryError oom) {
+- try {
+- oomParachuteData = null;
+- socket.getPoller().cancelledKey(key,SocketStatus.ERROR,false);
+- releaseCaches();
+- log.error("", oom);
+- }catch ( Throwable oomt ) {
+- try {
+- System.err.println(oomParachuteMsg);
+- oomt.printStackTrace();
+- }catch (Throwable letsHopeWeDontGetHere){}
+- }
+- }catch ( Throwable t ) {
+- log.error("",t);
+- socket.getPoller().cancelledKey(key,SocketStatus.ERROR,false);
+- } finally {
+- socket = null;
+- status = null;
+- //return to cache
+- processorCache.offer(this);
+- }
+- }
+-
+- }
+-
+- // ---------------------------------------------- TaskQueue Inner Class
+- public static class TaskQueue extends LinkedBlockingQueue<Runnable> {
+- ThreadPoolExecutor parent = null;
+-
+- public TaskQueue() {
+- super();
+- }
+-
+- public TaskQueue(int initialCapacity) {
+- super(initialCapacity);
+- }
+-
+- public TaskQueue(Collection<? extends Runnable> c) {
+- super(c);
+- }
+-
+-
+- public void setParent(ThreadPoolExecutor tp) {
+- parent = tp;
+- }
+-
+- public boolean offer(Runnable o) {
+- //we can't do any checks
+- if (parent==null) return super.offer(o);
+- //we are maxed out on threads, simply queue the object
+- if (parent.getPoolSize() == parent.getMaximumPoolSize()) return
super.offer(o);
+- //we have idle threads, just add it to the queue
+- //this is an approximation, so it could use some tuning
+- if (parent.getActiveCount()<(parent.getPoolSize())) return
super.offer(o);
+- //if we have less threads than maximum force creation of a new thread
+- if (parent.getPoolSize()<parent.getMaximumPoolSize()) return false;
+- //if we reached here, we need to add it to the queue
+- return super.offer(o);
+- }
+- }
+-
+- // ---------------------------------------------- ThreadFactory Inner Class
+- class TaskThreadFactory implements ThreadFactory {
+- final ThreadGroup group;
+- final AtomicInteger threadNumber = new AtomicInteger(1);
+- final String namePrefix;
+-
+- TaskThreadFactory(String namePrefix) {
+- SecurityManager s = System.getSecurityManager();
+- group = (s != null)? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
+- this.namePrefix = namePrefix;
+- }
+-
+- public Thread newThread(Runnable r) {
+- Thread t = new Thread(group, r, namePrefix +
threadNumber.getAndIncrement());
+- t.setDaemon(daemon);
+- t.setPriority(getThreadPriority());
+- return t;
+- }
+- }
+-
+- // ----------------------------------------------- SendfileData Inner Class
+-
+-
+- /**
+- * SendfileData class.
+- */
+- public static class SendfileData {
+- // File
+- public String fileName;
+- public FileChannel fchannel;
+- public long pos;
+- public long length;
+- // KeepAlive flag
+- public boolean keepAlive;
+- }
+-
+-}
+Index: java/org/apache/coyote/http11/Http11NioProtocol.java
+===================================================================
+--- java/org/apache/coyote/http11/Http11NioProtocol.java (revision 590752)
++++ java/org/apache/coyote/http11/Http11NioProtocol.java (working copy)
+@@ -1,849 +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.net.InetAddress;
+-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 javax.management.MBeanRegistration;
+-import javax.management.MBeanServer;
+-import javax.management.ObjectName;
+-
+-import org.apache.coyote.ActionCode;
+-import org.apache.coyote.ActionHook;
+-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.NioChannel;
+-import org.apache.tomcat.util.net.NioEndpoint;
+-import org.apache.tomcat.util.net.NioEndpoint.Handler;
+-import org.apache.tomcat.util.net.SSLImplementation;
+-import org.apache.tomcat.util.net.SecureNioChannel;
+-import org.apache.tomcat.util.net.SocketStatus;
+-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
+- * @author Filip Hanik
+- */
+-public class Http11NioProtocol implements ProtocolHandler, MBeanRegistration
+-{
+- protected SSLImplementation sslImplementation = null;
+-
+- public Http11NioProtocol() {
+- cHandler = new Http11ConnectionHandler( this );
+- setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
+- setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
+- //setServerSoTimeout(Constants.DEFAULT_SERVER_SOCKET_TIMEOUT);
+- setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
+- }
+-
+- /**
+- * The string manager for this package.
+- */
+- protected static StringManager sm =
+- StringManager.getManager(Constants.Package);
+-
+- /** 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 ) {
+- if( log.isTraceEnabled())
+- log.trace(sm.getString("http11protocol.getattribute", key));
+- return attributes.get(key);
+- }
+-
+- public Iterator getAttributeNames() {
+- return attributes.keySet().iterator();
+- }
+-
+- /**
+- * Set a property.
+- */
+- public boolean setProperty(String name, String value) {
+- setAttribute(name, value);
+- if ( name!=null && (name.startsWith("socket.")
||name.startsWith("selectorPool.")) ){
+- return ep.setProperty(name, value);
+- } else {
+- return ep.setProperty(name,value); //make sure we at least try to set all
properties
+- }
+- }
+-
+- /**
+- * Get a property
+- */
+- public String getProperty(String name) {
+- return (String)getAttribute(name);
+- }
+-
+- /** The adapter, used to call the connector
+- */
+- public void setAdapter(Adapter adapter) {
+- this.adapter=adapter;
+- }
+-
+- public Adapter getAdapter() {
+- return adapter;
+- }
+-
+-
+- /** Start the protocol
+- */
+- public void init() throws Exception {
+- ep.setName(getName());
+- ep.setHandler(cHandler);
+-
+- //todo, determine if we even need these
+-
ep.getSocketProperties().setRxBufSize(Math.max(ep.getSocketProperties().getRxBufSize(),getMaxHttpHeaderSize()));
+-
ep.getSocketProperties().setTxBufSize(Math.max(ep.getSocketProperties().getTxBufSize(),getMaxHttpHeaderSize()));
+-
+- try {
+- ep.init();
+- sslImplementation =
SSLImplementation.getInstance("org.apache.tomcat.util.net.jsse.JSSEImplementation");
+- } catch (Exception ex) {
+- log.error(sm.getString("http11protocol.endpoint.initerror"), ex);
+- throw ex;
+- }
+- if(log.isInfoEnabled())
+- log.info(sm.getString("http11protocol.init", getName()));
+-
+- }
+-
+- ObjectName tpOname;
+- ObjectName rgOname;
+-
+- public void start() throws Exception {
+- if( this.domain != null ) {
+- try {
+- tpOname=new ObjectName
+- (domain + ":" + "type=ThreadPool,name=" +
getName());
+- Registry.getRegistry(null, null)
+- .registerComponent(ep, 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 {
+- ep.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 {
+- ep.pause();
+- } catch (Exception ex) {
+- log.error(sm.getString("http11protocol.endpoint.pauseerror"),
ex);
+- throw ex;
+- }
+- if(log.isInfoEnabled())
+- log.info(sm.getString("http11protocol.pause", getName()));
+- }
+-
+- public void resume() throws Exception {
+- try {
+- ep.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()));
+- ep.destroy();
+- if( tpOname!=null )
+- Registry.getRegistry(null, null).unregisterComponent(tpOname);
+- if( rgOname != null )
+- Registry.getRegistry(null, null).unregisterComponent(rgOname);
+- }
+-
+- // -------------------- Properties--------------------
+- protected NioEndpoint ep=new NioEndpoint();
+- protected boolean secure = false;
+-
+- protected Hashtable attributes = new Hashtable();
+-
+- private int maxKeepAliveRequests=100; // as in Apache HTTPD server
+- private int timeout = 300000; // 5 minutes as in Apache HTTPD server
+- private int maxSavePostSize = 4 * 1024;
+- private int maxHttpHeaderSize = 8 * 1024;
+- protected int processorCache = 200; //max number of Http11NioProcessor objects
cached
+- private int socketCloseDelay=-1;
+- private boolean disableUploadTimeout = true;
+- private int socketBuffer = 9000;
+-
+- private Adapter adapter;
+- private Http11ConnectionHandler cHandler;
+-
+- /**
+- * Compression value.
+- */
+- private String compression = "off";
+- private String noCompressionUserAgents = null;
+- private String restrictedUserAgents = null;
+- private String compressableMimeTypes = "text/html,text/xml,text/plain";
+- private int compressionMinSize = 2048;
+-
+- private String server;
+-
+- // -------------------- Pool setup --------------------
+-
+- public void setPollerThreadCount(int count) {
+- ep.setPollerThreadCount(count);
+- }
+-
+- public int getPollerThreadCount() {
+- return ep.getPollerThreadCount();
+- }
+-
+- public void setSelectorTimeout(long timeout) {
+- ep.setSelectorTimeout(timeout);
+- }
+-
+- public long getSelectorTimeout() {
+- return ep.getSelectorTimeout();
+- }
+- // *
+- public Executor getExecutor() {
+- return ep.getExecutor();
+- }
+-
+- // *
+- public void setExecutor(Executor executor) {
+- ep.setExecutor(executor);
+- }
+-
+- public void setUseExecutor(boolean useexec) {
+- ep.setUseExecutor(useexec);
+- }
+-
+- public int getMaxThreads() {
+- return ep.getMaxThreads();
+- }
+-
+- public void setMaxThreads( int maxThreads ) {
+- ep.setMaxThreads(maxThreads);
+- setAttribute("maxThreads", "" + maxThreads);
+- }
+-
+- public void setThreadPriority(int threadPriority) {
+- ep.setThreadPriority(threadPriority);
+- setAttribute("threadPriority", "" + threadPriority);
+- }
+-
+- public void setAcceptorThreadPriority(int threadPriority) {
+- ep.setAcceptorThreadPriority(threadPriority);
+- setAttribute("acceptorThreadPriority", "" + threadPriority);
+- }
+-
+- public void setPollerThreadPriority(int threadPriority) {
+- ep.setPollerThreadPriority(threadPriority);
+- setAttribute("pollerThreadPriority", "" + threadPriority);
+- }
+-
+- public int getThreadPriority() {
+- return ep.getThreadPriority();
+- }
+-
+- public int getAcceptorThreadPriority() {
+- return ep.getAcceptorThreadPriority();
+- }
+-
+- public int getPollerThreadPriority() {
+- return ep.getThreadPriority();
+- }
+-
+-
+- public boolean getUseSendfile() {
+- return ep.getUseSendfile();
+- }
+-
+- public void setUseSendfile(boolean useSendfile) {
+- ep.setUseSendfile(useSendfile);
+- }
+-
+-
+- // -------------------- Tcp setup --------------------
+-
+- public int getBacklog() {
+- return ep.getBacklog();
+- }
+-
+- public void setBacklog( int i ) {
+- ep.setBacklog(i);
+- setAttribute("backlog", "" + i);
+- }
+-
+- public int getPort() {
+- return ep.getPort();
+- }
+-
+- public void setPort( int port ) {
+- ep.setPort(port);
+- setAttribute("port", "" + port);
+- }
+-
+- public InetAddress getAddress() {
+- return ep.getAddress();
+- }
+-
+- public void setAddress(InetAddress ia) {
+- ep.setAddress( ia );
+- setAttribute("address", "" + ia);
+- }
+-
+- public String getName() {
+- String encodedAddr = "";
+- if (getAddress() != null) {
+- encodedAddr = "" + getAddress();
+- if (encodedAddr.startsWith("/"))
+- encodedAddr = encodedAddr.substring(1);
+- encodedAddr = URLEncoder.encode(encodedAddr) + "-";
+- }
+- return ("http-" + encodedAddr + ep.getPort());
+- }
+-
+- public boolean getTcpNoDelay() {
+- return ep.getTcpNoDelay();
+- }
+-
+- public void setTcpNoDelay( boolean b ) {
+- ep.setTcpNoDelay( b );
+- setAttribute("tcpNoDelay", "" + b);
+- }
+-
+- public boolean getDisableUploadTimeout() {
+- return disableUploadTimeout;
+- }
+-
+- public void setDisableUploadTimeout(boolean isDisabled) {
+- disableUploadTimeout = isDisabled;
+- }
+-
+- public int getSocketBuffer() {
+- return socketBuffer;
+- }
+-
+- public void setSocketBuffer(int valueI) {
+- socketBuffer = valueI;
+- }
+-
+- public String getCompression() {
+- return compression;
+- }
+-
+- public void setCompression(String valueS) {
+- compression = valueS;
+- setAttribute("compression", valueS);
+- }
+-
+- public int getMaxSavePostSize() {
+- return maxSavePostSize;
+- }
+-
+- public void setMaxSavePostSize(int valueI) {
+- maxSavePostSize = valueI;
+- setAttribute("maxSavePostSize", "" + valueI);
+- }
+-
+- public int getMaxHttpHeaderSize() {
+- return maxHttpHeaderSize;
+- }
+-
+- public void setMaxHttpHeaderSize(int valueI) {
+- maxHttpHeaderSize = valueI;
+- setAttribute("maxHttpHeaderSize", "" + valueI);
+- }
+-
+- public String getRestrictedUserAgents() {
+- return restrictedUserAgents;
+- }
+-
+- public void setRestrictedUserAgents(String valueS) {
+- restrictedUserAgents = valueS;
+- setAttribute("restrictedUserAgents", valueS);
+- }
+-
+- public String getNoCompressionUserAgents() {
+- return noCompressionUserAgents;
+- }
+-
+- public void setNoCompressionUserAgents(String valueS) {
+- noCompressionUserAgents = valueS;
+- setAttribute("noCompressionUserAgents", valueS);
+- }
+-
+- public String getCompressableMimeType() {
+- return compressableMimeTypes;
+- }
+-
+- public void setCompressableMimeType(String valueS) {
+- compressableMimeTypes = valueS;
+- setAttribute("compressableMimeTypes", valueS);
+- }
+-
+- public int getCompressionMinSize() {
+- return compressionMinSize;
+- }
+-
+- public void setCompressionMinSize(int valueI) {
+- compressionMinSize = valueI;
+- setAttribute("compressionMinSize", "" + valueI);
+- }
+-
+- public int getSoLinger() {
+- return ep.getSoLinger();
+- }
+-
+- public void setSoLinger( int i ) {
+- ep.setSoLinger( i );
+- setAttribute("soLinger", "" + i);
+- }
+-
+- public int getSoTimeout() {
+- return ep.getSoTimeout();
+- }
+-
+- public void setSoTimeout( int i ) {
+- ep.setSoTimeout(i);
+- setAttribute("soTimeout", "" + i);
+- }
+-
+- public String getProtocol() {
+- return getProperty("protocol");
+- }
+-
+- public void setProtocol( String k ) {
+- setSecure(true);
+- setAttribute("protocol", k);
+- }
+-
+- public boolean getSecure() {
+- return secure;
+- }
+-
+- public void setSecure( boolean b ) {
+- ep.setSecure(b);
+- secure=b;
+- setAttribute("secure", "" + b);
+- }
+-
+- public int getMaxKeepAliveRequests() {
+- return maxKeepAliveRequests;
+- }
+-
+- /** Set the maximum number of Keep-Alive requests that we will honor.
+- */
+- public void setMaxKeepAliveRequests(int mkar) {
+- maxKeepAliveRequests = mkar;
+- setAttribute("maxKeepAliveRequests", "" + mkar);
+- }
+-
+- /**
+- * Return the Keep-Alive policy for the connection.
+- */
+- public boolean getKeepAlive() {
+- return ((maxKeepAliveRequests != 0) && (maxKeepAliveRequests != 1));
+- }
+-
+- /**
+- * Set the keep-alive policy for this connection.
+- */
+- public void setKeepAlive(boolean keepAlive) {
+- if (!keepAlive) {
+- setMaxKeepAliveRequests(1);
+- }
+- }
+-
+- public int getSocketCloseDelay() {
+- return socketCloseDelay;
+- }
+-
+- public void setSocketCloseDelay( int d ) {
+- socketCloseDelay=d;
+- setAttribute("socketCloseDelay", "" + d);
+- }
+-
+- public void setServer( String server ) {
+- this.server = server;
+- }
+-
+- public String getServer() {
+- return server;
+- }
+-
+- public int getTimeout() {
+- return timeout;
+- }
+-
+- public void setTimeout( int timeouts ) {
+- timeout = timeouts;
+- setAttribute("timeout", "" + timeouts);
+- }
+-
+- public void setProcessorCache(int processorCache) {
+- this.processorCache = processorCache;
+- }
+-
+- public void setOomParachute(int oomParachute) {
+- ep.setOomParachute(oomParachute);
+- setAttribute("oomParachute",oomParachute);
+- }
+-
+- // -------------------- SSL related properties --------------------
+-
+- public String getKeystoreFile() { return ep.getKeystoreFile();}
+- public void setKeystoreFile(String s ) { ep.setKeystoreFile(s);}
+- public void setKeystore(String s) { setKeystoreFile(s);}
+- public String getKeystore(){ return getKeystoreFile();}
+-
+- public String getAlgorithm() { return ep.getAlgorithm();}
+- public void setAlgorithm(String s ) { ep.setAlgorithm(s);}
+-
+- public void setClientauth(String s) {setClientAuth(s);}
+- public String getClientauth(){ return getClientAuth();}
+- public String getClientAuth() { return ep.getClientAuth();}
+- public void setClientAuth(String s ) { ep.setClientAuth(s);}
+-
+- public String getKeystorePass() { return ep.getKeystorePass();}
+- public void setKeystorePass(String s ) { ep.setKeystorePass(s);}
+- public void setKeypass(String s) { setKeystorePass(s);}
+- public String getKeypass() { return getKeystorePass();}
+- public String getKeystoreType() { return ep.getKeystoreType();}
+- public void setKeystoreType(String s ) { ep.setKeystoreType(s);}
+- public String getKeytype() { return getKeystoreType();}
+- public void setKeytype(String s ) { setKeystoreType(s);}
+-
+- public void setTruststoreFile(String f){ep.setTruststoreFile(f);}
+- public String getTruststoreFile(){return ep.getTruststoreFile();}
+- public void setTruststorePass(String p){ep.setTruststorePass(p);}
+- public String getTruststorePass(){return ep.getTruststorePass();}
+- public void setTruststoreType(String t){ep.setTruststoreType(t);}
+- public String getTruststoreType(){ return ep.getTruststoreType();}
+-
+-
+- public String getSslProtocol() { return ep.getSslProtocol();}
+- public void setSslProtocol(String s) { ep.setSslProtocol(s);}
+-
+- public String getCiphers() { return ep.getCiphers();}
+- public void setCiphers(String s) { ep.setCiphers(s);}
+-
+- public boolean getSSLEnabled() { return ep.isSSLEnabled(); }
+- public void setSSLEnabled(boolean SSLEnabled) { ep.setSSLEnabled(SSLEnabled); }
+-
+-
+-
+- // -------------------- Connection handler --------------------
+-
+- static class Http11ConnectionHandler implements Handler {
+-
+- protected Http11NioProtocol proto;
+- protected static int count = 0;
+- protected RequestGroupInfo global = new RequestGroupInfo();
+-
+- protected ConcurrentHashMap<NioChannel, Http11NioProcessor> connections =
+- new ConcurrentHashMap<NioChannel, Http11NioProcessor>();
+- protected ConcurrentLinkedQueue<Http11NioProcessor> recycledProcessors =
new ConcurrentLinkedQueue<Http11NioProcessor>() {
+- protected AtomicInteger size = new AtomicInteger(0);
+- 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) deregister(processor);
+- return result;
+- }
+-
+- public Http11NioProcessor poll() {
+- Http11NioProcessor result = super.poll();
+- if ( result != null ) {
+- size.decrementAndGet();
+- }
+- return result;
+- }
+-
+- public void clear() {
+- Http11NioProcessor next = poll();
+- while ( next != null ) {
+- deregister(next);
+- next = poll();
+- }
+- super.clear();
+- size.set(0);
+- }
+- };
+-
+- Http11ConnectionHandler(Http11NioProtocol proto) {
+- this.proto = proto;
+- }
+-
+- public void releaseCaches() {
+- recycledProcessors.clear();
+- }
+-
+- public SocketState event(NioChannel socket, SocketStatus status) {
+- Http11NioProcessor result = connections.get(socket);
+-
+- SocketState state = SocketState.CLOSED;
+- if (result != null) {
+- if (log.isDebugEnabled())
log.debug("Http11NioProcessor.error="+result.error);
+- // Call the appropriate event
+- try {
+- state = result.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(socket);
+- recycledProcessors.offer(result);
+- if (state == SocketState.OPEN) {
+- socket.getPoller().add(socket);
+- }
+- } else {
+- if (log.isDebugEnabled()) log.debug("Keeping
processor["+result);
+- socket.getPoller().add(socket);
+- }
+- }
+- }
+- return state;
+- }
+-
+- public SocketState process(NioChannel socket) {
+- Http11NioProcessor processor = null;
+- try {
+- if (processor == null) {
+- processor = recycledProcessors.poll();
+- }
+- if (processor == null) {
+- processor = createProcessor();
+- }
+-
+- if (processor instanceof ActionHook) {
+- ((ActionHook) processor).action(ActionCode.ACTION_START, null);
+- }
+-
+-
+- if (proto.ep.getSecure() && (proto.sslImplementation != null))
{
+- if (socket instanceof SecureNioChannel) {
+- SecureNioChannel ch = (SecureNioChannel)socket;
+-
processor.setSslSupport(proto.sslImplementation.getSSLSupport(ch.getSslEngine().getSession()));
+- }else processor.setSslSupport(null);
+- } else {
+- processor.setSslSupport(null);
+- }
+-
+-
+- SocketState state = processor.process(socket);
+- if (state == SocketState.LONG) {
+- // Associate the connection with the processor. The next request
+- // processed by this thread will use either a new or a recycled
+- // processor.
+- if (log.isDebugEnabled()) log.debug("Not recycling
["+processor+"]
Comet="+((NioEndpoint.KeyAttachment)socket.getAttachment(false)).getComet());
+- connections.put(socket, processor);
+- socket.getPoller().add(socket);
+- } else {
+- recycledProcessors.offer(processor);
+- }
+- return state;
+-
+- } 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);
+- }
+- recycledProcessors.offer(processor);
+- return SocketState.CLOSED;
+- }
+-
+- public Http11NioProcessor createProcessor() {
+- Http11NioProcessor processor = new Http11NioProcessor(
+- proto.ep.getSocketProperties().getRxBufSize(),
+- proto.ep.getSocketProperties().getTxBufSize(),
+- proto.maxHttpHeaderSize,
+- proto.ep);
+- processor.setAdapter(proto.adapter);
+- processor.setMaxKeepAliveRequests(proto.maxKeepAliveRequests);
+- processor.setTimeout(proto.timeout);
+- processor.setDisableUploadTimeout(proto.disableUploadTimeout);
+- processor.setCompression(proto.compression);
+- processor.setCompressionMinSize(proto.compressionMinSize);
+- processor.setNoCompressionUserAgents(proto.noCompressionUserAgents);
+- processor.setCompressableMimeTypes(proto.compressableMimeTypes);
+- processor.setRestrictedUserAgents(proto.restrictedUserAgents);
+- processor.setSocketBuffer(proto.socketBuffer);
+- processor.setMaxSavePostSize(proto.maxSavePostSize);
+- processor.setServer(proto.server);
+- register(processor);
+- return processor;
+- }
+- AtomicInteger registerCount = new AtomicInteger(0);
+- public void register(Http11NioProcessor processor) {
+- if (proto.getDomain() != null) {
+- synchronized (this) {
+- try {
+- registerCount.addAndGet(1);
+- if (log.isDebugEnabled()) log.debug("Register
["+processor+"] count="+registerCount.get());
+- RequestInfo rp = processor.getRequest().getRequestProcessor();
+- rp.setGlobalProcessor(global);
+- ObjectName rpName = new ObjectName
+- (proto.getDomain() +
":type=RequestProcessor,worker="
+- + proto.getName() + ",name=HttpRequest" +
count++);
+- Registry.getRegistry(null, null).registerComponent(rp, rpName,
null);
+- rp.setRpName(rpName);
+- } catch (Exception e) {
+- log.warn("Error registering request");
+- }
+- }
+- }
+- }
+-
+- public void deregister(Http11NioProcessor processor) {
+- if (proto.getDomain() != null) {
+- synchronized (this) {
+- try {
+- registerCount.addAndGet(-1);
+- if (log.isDebugEnabled()) log.debug("Deregister
["+processor+"] count="+registerCount.get());
+- RequestInfo rp = processor.getRequest().getRequestProcessor();
+- rp.setGlobalProcessor(null);
+- ObjectName rpName = rp.getRpName();
+- Registry.getRegistry(null, null).unregisterComponent(rpName);
+- rp.setRpName(null);
+- } catch (Exception e) {
+- log.warn("Error unregistering request", e);
+- }
+- }
+- }
+- }
+-
+- }
+-
+-
+-
+- protected static org.apache.juli.logging.Log log
+- = org.apache.juli.logging.LogFactory.getLog(Http11NioProtocol.class);
+-
+- // -------------------- Various implementation classes --------------------
+-
+- protected String domain;
+- protected ObjectName oname;
+- protected MBeanServer mserver;
+-
+- public ObjectName getObjectName() {
+- return oname;
+- }
+-
+- public String getDomain() {
+- return domain;
+- }
+-
+- public int getProcessorCache() {
+- return processorCache;
+- }
+-
+- public int getOomParachute() {
+- return ep.getOomParachute();
+- }
+-
+- 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() {
+- }
+-}
+Index: java/org/apache/coyote/http11/Http11NioProcessor.java
+===================================================================
+--- java/org/apache/coyote/http11/Http11NioProcessor.java (revision 590752)
++++ java/org/apache/coyote/http11/Http11NioProcessor.java (working copy)
+@@ -1,1813 +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.InterruptedIOException;
+-import java.net.InetAddress;
+-import java.nio.channels.SelectionKey;
+-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.NioChannel;
+-import org.apache.tomcat.util.net.NioEndpoint;
+-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;
+-import org.apache.tomcat.util.net.NioEndpoint.KeyAttachment;
+-
+-
+-/**
+- * Processes HTTP requests.
+- *
+- * @author Remy Maucherat
+- * @author Filip Hanik
+- */
+-public class Http11NioProcessor implements ActionHook {
+-
+-
+- /**
+- * Logger.
+- */
+- protected static org.apache.juli.logging.Log log
+- = org.apache.juli.logging.LogFactory.getLog(Http11NioProcessor.class);
+-
+- /**
+- * The string manager for this package.
+- */
+- protected static StringManager sm =
+- StringManager.getManager(Constants.Package);
+-
+- /**
+- * SSL information.
+- */
+- protected SSLSupport sslSupport;
+-
+- // ----------------------------------------------------------- Constructors
+-
+-
+- public Http11NioProcessor(int rxBufSize, int txBufSize, int maxHttpHeaderSize,
NioEndpoint endpoint) {
+-
+- this.endpoint = endpoint;
+-
+- request = new Request();
+- int readTimeout = endpoint.getSoTimeout();
+- inputBuffer = new InternalNioInputBuffer(request,
maxHttpHeaderSize,readTimeout);
+- request.setInputBuffer(inputBuffer);
+-
+- response = new Response();
+- response.setHook(this);
+- outputBuffer = new InternalNioOutputBuffer(response,
maxHttpHeaderSize,readTimeout);
+- response.setOutputBuffer(outputBuffer);
+- request.setResponse(response);
+-
+- ssl = endpoint.isSSLEnabled();
+-
+- 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 InternalNioInputBuffer inputBuffer = null;
+-
+-
+- /**
+- * Output.
+- */
+- protected InternalNioOutputBuffer 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;
+-
+- /**
+- * Sendfile data.
+- */
+- protected NioEndpoint.SendfileData sendfileData = null;
+-
+- /**
+- * Comet used.
+- */
+- protected boolean comet = false;
+-
+- /**
+- * Closed flag, a Comet async thread can
+- * signal for this Nio processor to be closed and recycled instead
+- * of waiting for a timeout.
+- * Closed by HttpServletResponse.getWriter().close()
+- */
+- protected boolean cometClose = 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;
+-
+-
+- /**
+- * SSL enabled ?
+- */
+- protected boolean ssl = false;
+-
+-
+- /**
+- * Socket associated with the current connection.
+- */
+- protected NioChannel socket = null;
+-
+-
+- /**
+- * 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;
+-
+-
+- /**
+- * Maximum timeout on uploads. 5 minutes as in Apache HTTPD server.
+- */
+- protected int timeout = 300000;
+-
+-
+- /**
+- * 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 save 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 NioEndpoint endpoint;
+-
+-
+- /**
+- * Allow a customized the server header for the tin-foil hat folks.
+- */
+- protected String server = null;
+-
+-
+- // ------------------------------------------------------------- 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) {
+- StringTokenizer st = new StringTokenizer(compressableMimeTypes,
",");
+-
+- while (st.hasMoreTokens()) {
+- addCompressableMimeType(st.nextToken().trim());
+- }
+- }
+- }
+-
+-
+- /**
+- * Return the list of restricted user agents.
+- */
+- public String[] findCompressableMimeTypes() {
+- return (compressableMimeTypes);
+- }
+-
+-
+-
+- // --------------------------------------------------------- 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;
+- }
+-
+-
+- /**
+- * General use method
+- *
+- * @param sArray the StringArray
+- * @param value string
+- */
+- private boolean inStringArray(String sArray[], String value) {
+- for (int i = 0; i < sArray.length; i++) {
+- if (sArray[i].equals(value)) {
+- return true;
+- }
+- }
+- return false;
+- }
+-
+-
+- /**
+- * 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 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 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;
+- }
+-
+- /**
+- * 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 upload timeout.
+- */
+- public void setTimeout( int timeouts ) {
+- timeout = timeouts ;
+- }
+-
+- /**
+- * Get the upload timeout.
+- */
+- public int getTimeout() {
+- return timeout;
+- }
+-
+-
+- /**
+- * 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;
+- }
+-
+- /**
+- * Process pipelined HTTP requests using the specified input and output
+- * streams.
+- *
+- * @throws IOException error during an I/O operation
+- */
+- public SocketState event(SocketStatus status)
+- throws IOException {
+-
+- RequestInfo rp = request.getRequestProcessor();
+-
+- try {
+- rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
+- error = !adapter.event(request, response, status);
+- if ( !error ) {
+- NioEndpoint.KeyAttachment attach =
(NioEndpoint.KeyAttachment)socket.getAttachment(false);
+- if (attach != null) {
+- attach.setComet(comet);
+- if (comet) {
+- Integer comettimeout = (Integer)
request.getAttribute("org.apache.tomcat.comet.timeout");
+- if (comettimeout != null)
attach.setTimeout(comettimeout.longValue());
+- } else {
+- //reset the timeout
+-
attach.setTimeout(endpoint.getSocketProperties().getSoTimeout());
+- }
+-
+- }
+- }
+- } 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 (!comet) {
+- 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(NioChannel 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 socket
+- this.socket = socket;
+- inputBuffer.setSocket(socket);
+- outputBuffer.setSocket(socket);
+- inputBuffer.setSelectorPool(endpoint.getSelectorPool());
+- outputBuffer.setSelectorPool(endpoint.getSelectorPool());
+-
+- // Error flag
+- error = false;
+- keepAlive = true;
+- comet = false;
+-
+-
+- int keepAliveLeft = maxKeepAliveRequests;
+- long soTimeout = endpoint.getSoTimeout();
+-
+- int limit = 0;
+-
+- boolean keptAlive = false;
+- boolean openSocket = false;
+- boolean recycle = true;
+- while (!error && keepAlive && !comet) {
+-
+- // Parsing the request header
+- try {
+- if( !disableUploadTimeout && keptAlive && soTimeout >
0 ) {
+- socket.getIOChannel().socket().setSoTimeout((int)soTimeout);
+- inputBuffer.readTimeout = soTimeout;
+- }
+- if (!inputBuffer.parseRequestLine(keptAlive &&
(endpoint.getCurrentThreadsBusy() >= limit))) {
+- // This means that no data is available right now
+- // (long keepalive), so that the processor should be recycled
+- // and the method should return true
+- openSocket = true;
+- // Add the socket to the poller
+- socket.getPoller().add(socket);
+- break;
+- }
+- keptAlive = true;
+- if ( !inputBuffer.parseHeaders() ) {
+- openSocket = true;
+- socket.getPoller().add(socket);
+- recycle = false;
+- break;
+- }
+- request.setStartTime(System.currentTimeMillis());
+- if (!disableUploadTimeout) { //only for body, not for request headers
+- socket.getIOChannel().socket().setSoTimeout((int)timeout);
+- inputBuffer.readTimeout = soTimeout;
+- }
+- } 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());
+- }
+- // Comet support
+- SelectionKey key =
socket.getIOChannel().keyFor(socket.getPoller().getSelector());
+- if (key != null) {
+- NioEndpoint.KeyAttachment attach = (NioEndpoint.KeyAttachment)
key.attachment();
+- if (attach != null) {
+- attach.setComet(comet);
+- if (comet) {
+- Integer comettimeout = (Integer)
request.getAttribute("org.apache.tomcat.comet.timeout");
+- if (comettimeout != null)
attach.setTimeout(comettimeout.longValue());
+- }
+- }
+- }
+- } 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 (!comet) {
+- 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 (!comet) {
+- // Next request
+- inputBuffer.nextRequest();
+- outputBuffer.nextRequest();
+- }
+-
+- // Do sendfile as needed: add socket to sendfile and end
+- if (sendfileData != null && !error) {
+- KeyAttachment ka = (KeyAttachment)socket.getAttachment(false);
+- ka.setSendfileData(sendfileData);
+- sendfileData.keepAlive = keepAlive;
+- SelectionKey key =
socket.getIOChannel().keyFor(socket.getPoller().getSelector());
+- //do the first write on this thread, might as well
+- openSocket = socket.getPoller().processSendfile(key,ka,true);
+- break;
+- }
+-
+-
+- rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
+-
+- }
+-
+- rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
+-
+- if (comet) {
+- if (error) {
+- recycle();
+- return SocketState.CLOSED;
+- } else {
+- return SocketState.LONG;
+- }
+- } else {
+- if ( recycle ) recycle();
+- return (openSocket) ? SocketState.OPEN : 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;
+- this.cometClose = false;
+- this.comet = false;
+- }
+-
+-
+- // ----------------------------------------------------- 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
+-
+- try {
+- prepareResponse();
+- 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
+-
+- comet = false;
+- cometClose = true;
+- SelectionKey key =
socket.getIOChannel().keyFor(socket.getPoller().getSelector());
+- if ( key != null ) {
+- NioEndpoint.KeyAttachment attach = (NioEndpoint.KeyAttachment)
key.attachment();
+- if ( attach!=null && attach.getComet()) {
+- //if this is a comet connection
+- //then execute the connection closure at the next selector loop
+-
request.getAttributes().remove("org.apache.tomcat.comet.timeout");
+- //attach.setTimeout(5000); //force a cleanup in 5 seconds
+- //attach.setError(true); //this has caused concurrency errors
+- }
+- }
+-
+- try {
+- outputBuffer.endRequest();
+- } catch (IOException e) {
+- // Set error flag
+- error = true;
+- }
+-
+- } else if (actionCode == ActionCode.ACTION_RESET) {
+-
+- // Reset response
+-
+- // Note: This must be called before the response is committed
+-
+- outputBuffer.reset();
+-
+- } else if (actionCode == ActionCode.ACTION_CUSTOM) {
+-
+- // Do nothing
+-
+- } else if (actionCode == ActionCode.ACTION_REQ_HOST_ADDR_ATTRIBUTE) {
+-
+- // Get remote host address
+- if ((remoteAddr == null) && (socket != null)) {
+- InetAddress inetAddr = socket.getIOChannel().socket().getInetAddress();
+- if (inetAddr != null) {
+- remoteAddr = inetAddr.getHostAddress();
+- }
+- }
+- request.remoteAddr().setString(remoteAddr);
+-
+- } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_NAME_ATTRIBUTE) {
+-
+- // Get local host name
+- if ((localName == null) && (socket != null)) {
+- InetAddress inetAddr =
socket.getIOChannel().socket().getLocalAddress();
+- if (inetAddr != null) {
+- localName = inetAddr.getHostName();
+- }
+- }
+- request.localName().setString(localName);
+-
+- } else if (actionCode == ActionCode.ACTION_REQ_HOST_ATTRIBUTE) {
+-
+- // Get remote host name
+- if ((remoteHost == null) && (socket != null)) {
+- InetAddress inetAddr = socket.getIOChannel().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.getIOChannel().socket().getLocalAddress().getHostAddress();
+-
+- request.localAddr().setString(localAddr);
+-
+- } else if (actionCode == ActionCode.ACTION_REQ_REMOTEPORT_ATTRIBUTE) {
+-
+- if ((remotePort == -1 ) && (socket !=null)) {
+- remotePort = socket.getIOChannel().socket().getPort();
+- }
+- request.setRemotePort(remotePort);
+-
+- } else if (actionCode == ActionCode.ACTION_REQ_LOCALPORT_ATTRIBUTE) {
+-
+- if ((localPort == -1 ) && (socket !=null)) {
+- localPort = socket.getIOChannel().socket().getLocalPort();
+- }
+- request.setLocalPort(localPort);
+-
+- } else if (actionCode == ActionCode.ACTION_REQ_SSL_ATTRIBUTE ) {
+-
+- try {
+- if (sslSupport != null) {
+- Object sslO = sslSupport.getCipherSuite();
+- if (sslO != null)
+- request.setAttribute
+- (SSLSupport.CIPHER_SUITE_KEY, sslO);
+- sslO = sslSupport.getPeerCertificateChain(false);
+- if (sslO != null)
+- request.setAttribute
+- (SSLSupport.CERTIFICATE_KEY, sslO);
+- sslO = sslSupport.getKeySize();
+- if (sslO != null)
+- request.setAttribute
+- (SSLSupport.KEY_SIZE_KEY, sslO);
+- sslO = sslSupport.getSessionId();
+- if (sslO != null)
+- request.setAttribute
+- (SSLSupport.SESSION_ID_KEY, sslO);
+- }
+- } catch (Exception e) {
+- log.warn(sm.getString("http11processor.socket.ssl"), e);
+- }
+-
+- } else if (actionCode == ActionCode.ACTION_REQ_SSL_CERTIFICATE) {
+-
+- if( sslSupport != null) {
+- /*
+- * Consume and buffer the request body, so that it does not
+- * interfere with the client's handshake messages
+- */
+- InputFilter[] inputFilters = inputBuffer.getFilters();
+- ((BufferedInputFilter) inputFilters[Constants.BUFFERED_FILTER])
+- .setLimit(maxSavePostSize);
+- inputBuffer.addActiveFilter
+- (inputFilters[Constants.BUFFERED_FILTER]);
+- try {
+- Object sslO = sslSupport.getPeerCertificateChain(true);
+- if( sslO != null) {
+- request.setAttribute
+- (SSLSupport.CERTIFICATE_KEY, sslO);
+- }
+- } catch (Exception e) {
+- log.warn(sm.getString("http11processor.socket.ssl"), e);
+- }
+- }
+-
+- } else if (actionCode == ActionCode.ACTION_REQ_SET_BODY_REPLAY) {
+- ByteChunk body = (ByteChunk) param;
+-
+- InputFilter savedBody = new SavedRequestInputFilter(body);
+- savedBody.setRequest(request);
+-
+- InternalNioInputBuffer internalBuffer = (InternalNioInputBuffer)
+- request.getInputBuffer();
+- internalBuffer.addActiveFilter(savedBody);
+-
+- } else if (actionCode == ActionCode.ACTION_AVAILABLE) {
+- request.setAvailable(inputBuffer.available());
+- } else if (actionCode == ActionCode.ACTION_COMET_BEGIN) {
+- comet = true;
+- } else if (actionCode == ActionCode.ACTION_COMET_END) {
+- comet = false;
+- }
+-
+- }
+-
+-
+- // ------------------------------------------------------ Connector Methods
+-
+-
+- /**
+- * Set the associated adapter.
+- *
+- * @param adapter the new adapter
+- */
+- public void setAdapter(Adapter adapter) {
+- this.adapter = adapter;
+- }
+-
+- public void setSslSupport(SSLSupport sslSupport) {
+- this.sslSupport = sslSupport;
+- }
+-
+- /**
+- * Get the associated adapter.
+- *
+- * @return the associated adapter
+- */
+- public Adapter getAdapter() {
+- return adapter;
+- }
+-
+- public SSLSupport getSslSupport() {
+- return sslSupport;
+- }
+-
+- // ------------------------------------------------------ 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;
+- sendfileData = null;
+- if (ssl) {
+- 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().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().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;
+- }
+-
+- // Advertise sendfile support through a request attribute
+- if (endpoint.getUseSendfile())
+- request.setAttribute("org.apache.tomcat.sendfile.support",
Boolean.TRUE);
+- // Advertise comet support through a request attribute
+- request.setAttribute("org.apache.tomcat.comet.support",
Boolean.TRUE);
+- // Advertise comet timeout support
+- request.setAttribute("org.apache.tomcat.comet.timeout.support",
Boolean.TRUE);
+-
+- }
+-
+-
+- /**
+- * Parse host.
+- */
+- public 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 (!ssl) {
+- // 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[(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);
+-
+- }
+-
+- }
+-
+-
+- /**
+- * 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() throws IOException {
+-
+- 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;
+- }
+-
+- // Sendfile support
+- if (this.endpoint.getUseSendfile()) {
+- String fileName = (String)
request.getAttribute("org.apache.tomcat.sendfile.filename");
+- if (fileName != null) {
+- // No entity body sent here
+- outputBuffer.addActiveFilter(outputFilters[Constants.VOID_FILTER]);
+- contentDelimitation = true;
+- sendfileData = new NioEndpoint.SendfileData();
+- sendfileData.fileName = fileName;
+- sendfileData.pos = ((Long)
request.getAttribute("org.apache.tomcat.sendfile.start")).longValue();
+- sendfileData.length = ((Long)
request.getAttribute("org.apache.tomcat.sendfile.end")).longValue() -
sendfileData.pos;
+- }
+- }
+-
+-
+-
+- // Check for compression
+- boolean useCompression = false;
+- if (entityBody && (compressionLevel > 0) && (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) {
+- 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.setValue("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 buffered input filter
+- inputBuffer.addFilter(new BufferedInputFilter());
+-
+- // 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 */;
+- }
+-
+-}
+Index: java/org/apache/coyote/http11/InternalNioInputBuffer.java
+===================================================================
+--- java/org/apache/coyote/http11/InternalNioInputBuffer.java (revision 590752)
++++ java/org/apache/coyote/http11/InternalNioInputBuffer.java (working copy)
+@@ -1,908 +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.EOFException;
+-import java.io.IOException;
+-import java.nio.channels.Selector;
+-
+-import org.apache.coyote.InputBuffer;
+-import org.apache.coyote.Request;
+-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.net.NioChannel;
+-import org.apache.tomcat.util.net.NioSelectorPool;
+-import org.apache.tomcat.util.res.StringManager;
+-
+-/**
+- * Implementation of InputBuffer which provides HTTP request header parsing as
+- * well as transfer decoding.
+- *
+- * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+- * @author Filip Hanik
+- */
+-public class InternalNioInputBuffer implements InputBuffer {
+-
+-
+- // -------------------------------------------------------------- Constants
+-
+- enum HeaderParseStatus {DONE, HAVE_MORE_HEADERS, NEED_MORE_DATA}
+- enum HeaderParsePosition {HEADER_START, HEADER_NAME, HEADER_VALUE,
HEADER_MULTI_LINE}
+- // ----------------------------------------------------------- Constructors
+-
+-
+- /**
+- * Alternate constructor.
+- */
+- public InternalNioInputBuffer(Request request, int headerBufferSize,
+- long readTimeout) {
+-
+- this.request = request;
+- headers = request.getMimeHeaders();
+-
+- buf = new byte[headerBufferSize];
+-// if (headerBufferSize < (8 * 1024)) {
+-// bbuf = ByteBuffer.allocateDirect(6 * 1500);
+-// } else {
+-// bbuf = ByteBuffer.allocateDirect((headerBufferSize / 1500 + 1) * 1500);
+-// }
+-
+- inputStreamInputBuffer = new SocketInputBuffer();
+-
+- filterLibrary = new InputFilter[0];
+- activeFilters = new InputFilter[0];
+- lastActiveFilter = -1;
+-
+- parsingHeader = true;
+- parsingRequestLine = true;
+- headerParsePos = HeaderParsePosition.HEADER_START;
+- headerData.recycle();
+- swallowInput = true;
+-
+- if (readTimeout < 0) {
+- this.readTimeout = -1;
+- } else {
+- this.readTimeout = readTimeout;
+- }
+-
+- }
+-
+-
+- // -------------------------------------------------------------- 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;
+- protected boolean parsingRequestLine;
+- protected HeaderParsePosition headerParsePos;
+-
+-
+- /**
+- * 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 socket.
+- */
+- protected NioChannel socket;
+-
+- /**
+- * Selector pool, for blocking reads and blocking writes
+- */
+- protected NioSelectorPool pool;
+-
+-
+- /**
+- * 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;
+-
+-
+- /**
+- * The socket timeout used when reading the first block of the request
+- * header.
+- */
+- protected long readTimeout;
+-
+- // ------------------------------------------------------------- Properties
+-
+-
+- /**
+- * Set the underlying socket.
+- */
+- public void setSocket(NioChannel socket) {
+- this.socket = socket;
+- }
+-
+- /**
+- * Get the underlying socket input stream.
+- */
+- public NioChannel getSocket() {
+- return socket;
+- }
+-
+- public void setSelectorPool(NioSelectorPool pool) {
+- this.pool = pool;
+- }
+-
+- public NioSelectorPool getSelectorPool() {
+- return pool;
+- }
+-
+-
+- /**
+- * Add an input filter to the filter library.
+- */
+- public void addFilter(InputFilter filter) {
+-
+- 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 filters
+- for (int i = 0; i <= lastActiveFilter; i++) {
+- activeFilters[i].recycle();
+- }
+-
+- // Recycle Request object
+- request.recycle();
+-
+- socket = null;
+- lastValid = 0;
+- pos = 0;
+- lastActiveFilter = -1;
+- parsingHeader = true;
+- headerParsePos = HeaderParsePosition.HEADER_START;
+- parsingRequestLine = true;
+- headerData.recycle();
+- 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 void 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;
+- headerParsePos = HeaderParsePosition.HEADER_START;
+- parsingRequestLine = true;
+- headerData.recycle();
+- swallowInput = true;
+-
+- }
+-
+-
+- /**
+- * 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.
+- * @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 {
+-
+- //check state
+- if ( !parsingRequestLine ) return true;
+-
+- int start = 0;
+-
+- //
+- // Skipping blank lines
+- //
+-
+- byte chr = 0;
+- do {
+-
+- // Read new bytes if needed
+- if (pos >= lastValid) {
+- if (useAvailableData) {
+- return false;
+- }
+- if (readTimeout == -1) {
+- if (!fill(false,true)) //request line parsing
+- throw new
EOFException(sm.getString("iib.eof.error"));
+- } else {
+- // Do a simple read with a short timeout
+- if ( !readSocket(true, false) ) return false;
+- }
+- }
+-
+- 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 (readTimeout == -1) {
+- if (!fill(false,true)) //request line parsing
+- return false;
+- } else {
+- // Do a simple read with a short timeout
+- if ( !readSocket(true, true) ) return false;
+- }
+- }
+-
+- //
+- // 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(true,true)) //request line parsing
+- return false;
+- }
+-
+- if (buf[pos] == Constants.SP) {
+- space = true;
+- request.method().setBytes(buf, start, pos - start);
+- }
+-
+- pos++;
+-
+- }
+-
+- // Mark the current buffer position
+- start = pos;
+- int end = 0;
+- int questionPos = -1;
+-
+- //
+- // Reading the URI
+- //
+-
+- space = false;
+- boolean eol = false;
+-
+- while (!space) {
+-
+- // Read new bytes if needed
+- if (pos >= lastValid) {
+- if (!fill(true,true)) //request line parsing
+- return false;
+- }
+-
+- if (buf[pos] == Constants.SP) {
+- 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);
+- }
+-
+- // 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(true,true)) //reques line parsing
+- return false;
+- }
+-
+- 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("");
+- }
+- parsingRequestLine = false;
+- return true;
+-
+- }
+-
+- private void expand(int newsize) {
+- if ( newsize > buf.length ) {
+- byte[] tmp = new byte[newsize];
+- System.arraycopy(buf,0,tmp,0,buf.length);
+- buf = tmp;
+- tmp = null;
+- }
+- }
+- /**
+- * Perform blocking read with a timeout if desired
+- * @param timeout boolean - if we want to use the timeout data
+- * @param block - true if the system should perform a blocking read, false
otherwise
+- * @return boolean - true if data was read, false is no data read, EOFException if
EOF is reached
+- * @throws IOException if a socket exception occurs
+- * @throws EOFException if end of stream is reached
+- */
+- private boolean readSocket(boolean timeout, boolean block) throws IOException {
+- int nRead = 0;
+- long rto = timeout?this.readTimeout:-1;
+- socket.getBufHandler().getReadBuffer().clear();
+- if ( block ) {
+- Selector selector = null;
+- try { selector = getSelectorPool().get(); }catch ( IOException x ) {}
+- try {
+- nRead =
getSelectorPool().read(socket.getBufHandler().getReadBuffer(),socket,selector,rto);
+- } catch ( EOFException eof ) {
+- nRead = -1;
+- } finally {
+- if ( selector != null ) getSelectorPool().put(selector);
+- }
+- } else {
+- nRead = socket.read(socket.getBufHandler().getReadBuffer());
+- }
+- if (nRead > 0) {
+- socket.getBufHandler().getReadBuffer().flip();
+- socket.getBufHandler().getReadBuffer().limit(nRead);
+- expand(nRead + pos);
+- socket.getBufHandler().getReadBuffer().get(buf, pos, nRead);
+- lastValid = pos + nRead;
+- return true;
+- } else if (nRead == -1) {
+- //return false;
+- throw new EOFException(sm.getString("iib.eof.error"));
+- } else {
+- return false;
+- }
+- }
+-
+- /**
+- * Parse the HTTP headers.
+- */
+- public boolean parseHeaders()
+- throws IOException {
+- HeaderParseStatus status = HeaderParseStatus.HAVE_MORE_HEADERS;
+-
+- do {
+- status = parseHeader();
+- } while ( status == HeaderParseStatus.HAVE_MORE_HEADERS );
+- if (status == HeaderParseStatus.DONE) {
+- parsingHeader = false;
+- end = pos;
+- return true;
+- } else {
+- return false;
+- }
+- }
+-
+-
+- /**
+- * Parse an HTTP header.
+- *
+- * @return false after reading a blank line (which indicates that the
+- * HTTP header parsing is done
+- */
+- public HeaderParseStatus parseHeader()
+- throws IOException {
+-
+- //
+- // Check for blank line
+- //
+-
+- byte chr = 0;
+- while (headerParsePos == HeaderParsePosition.HEADER_START) {
+-
+- // Read new bytes if needed
+- if (pos >= lastValid) {
+- if (!fill(true,true)) {//parse header
+- headerParsePos = HeaderParsePosition.HEADER_START;
+- return HeaderParseStatus.NEED_MORE_DATA;
+- }
+- }
+-
+- chr = buf[pos];
+-
+- if ((chr == Constants.CR) || (chr == Constants.LF)) {
+- if (chr == Constants.LF) {
+- pos++;
+- return HeaderParseStatus.DONE;
+- }
+- } else {
+- break;
+- }
+-
+- pos++;
+-
+- }
+-
+- if ( headerParsePos == HeaderParsePosition.HEADER_START ) {
+- // Mark the current buffer position
+- headerData.start = pos;
+- headerParsePos = HeaderParsePosition.HEADER_NAME;
+- }
+-
+- //
+- // Reading the header name
+- // Header name is always US-ASCII
+- //
+-
+-
+-
+- while (headerParsePos == HeaderParsePosition.HEADER_NAME) {
+-
+- // Read new bytes if needed
+- if (pos >= lastValid) {
+- if (!fill(true,true)) { //parse header
+- return HeaderParseStatus.NEED_MORE_DATA;
+- }
+- }
+-
+- if (buf[pos] == Constants.COLON) {
+- headerParsePos = HeaderParsePosition.HEADER_VALUE;
+- headerData.headerValue = headers.addValue(buf, headerData.start, pos -
headerData.start);
+- }
+- chr = buf[pos];
+- if ((chr >= Constants.A) && (chr <= Constants.Z)) {
+- buf[pos] = (byte) (chr - Constants.LC_OFFSET);
+- }
+-
+- pos++;
+- if ( headerParsePos == HeaderParsePosition.HEADER_VALUE ) {
+- // Mark the current buffer position
+- headerData.start = pos;
+- headerData.realPos = pos;
+- }
+- }
+-
+-
+- //
+- // Reading the header value (which can be spanned over multiple lines)
+- //
+-
+- boolean eol = false;
+-
+- while (headerParsePos == HeaderParsePosition.HEADER_VALUE ||
+- headerParsePos == HeaderParsePosition.HEADER_MULTI_LINE) {
+- if ( headerParsePos == HeaderParsePosition.HEADER_VALUE ) {
+-
+- boolean space = true;
+-
+- // Skipping spaces
+- while (space) {
+-
+- // Read new bytes if needed
+- if (pos >= lastValid) {
+- if (!fill(true,true)) {//parse header
+- //HEADER_VALUE, should already be set
+- return HeaderParseStatus.NEED_MORE_DATA;
+- }
+- }
+-
+- if ((buf[pos] == Constants.SP) || (buf[pos] == Constants.HT)) {
+- pos++;
+- } else {
+- space = false;
+- }
+-
+- }
+-
+- headerData.lastSignificantChar = headerData.realPos;
+-
+- // Reading bytes until the end of the line
+- while (!eol) {
+-
+- // Read new bytes if needed
+- if (pos >= lastValid) {
+- if (!fill(true,true)) {//parse header
+- //HEADER_VALUE
+- return HeaderParseStatus.NEED_MORE_DATA;
+- }
+-
+- }
+-
+- if (buf[pos] == Constants.CR) {
+- } else if (buf[pos] == Constants.LF) {
+- eol = true;
+- } else if (buf[pos] == Constants.SP) {
+- buf[headerData.realPos] = buf[pos];
+- headerData.realPos++;
+- } else {
+- buf[headerData.realPos] = buf[pos];
+- headerData.realPos++;
+- headerData.lastSignificantChar = headerData.realPos;
+- }
+-
+- pos++;
+-
+- }
+-
+- headerData.realPos = headerData.lastSignificantChar;
+-
+- // Checking the first character of the new line. If the character
+- // is a LWS, then it's a multiline header
+- headerParsePos = HeaderParsePosition.HEADER_MULTI_LINE;
+- }
+- // Read new bytes if needed
+- if (pos >= lastValid) {
+- if (!fill(true,true)) {//parse header
+-
+- //HEADER_MULTI_LINE
+- return HeaderParseStatus.NEED_MORE_DATA;
+- }
+- }
+-
+- chr = buf[pos];
+- if ( headerParsePos == HeaderParsePosition.HEADER_MULTI_LINE ) {
+- if ( (chr != Constants.SP) && (chr != Constants.HT)) {
+- headerParsePos = HeaderParsePosition.HEADER_START;
+- } else {
+- eol = false;
+- // Copying one extra space in the buffer (since there must
+- // be at least one space inserted between the lines)
+- buf[headerData.realPos] = chr;
+- headerData.realPos++;
+- }
+- }
+- }
+- // Set the header value
+- headerData.headerValue.setBytes(buf, headerData.start, headerData.realPos -
headerData.start);
+- headerData.recycle();
+- return HeaderParseStatus.HAVE_MORE_HEADERS;
+- }
+-
+- protected HeaderParseData headerData = new HeaderParseData();
+- public static class HeaderParseData {
+- int start = 0;
+- int realPos = 0;
+- int lastSignificantChar = 0;
+- MessageBytes headerValue = null;
+- public void recycle() {
+- start = 0;
+- realPos = 0;
+- lastSignificantChar = 0;
+- headerValue = null;
+- }
+- }
+-
+-
+- /**
+- * Available bytes (note that due to encoding, this may not correspond )
+- */
+- public int available() {
+- int result = (lastValid - pos);
+- if ((result == 0) && (lastActiveFilter >= 0)) {
+- for (int i = 0; (result == 0) && (i <= lastActiveFilter); i++) {
+- result = activeFilters[i].available();
+- }
+- }
+- return result;
+- }
+-
+-
+- // ---------------------------------------------------- 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(boolean timeout, boolean block)
+- throws IOException, EOFException {
+-
+- boolean read = false;
+-
+- if (parsingHeader) {
+-
+- if (lastValid == buf.length) {
+- throw new IOException
+- (sm.getString("iib.requestheadertoolarge.error"));
+- }
+-
+- // Do a simple read with a short timeout
+- read = readSocket(timeout,block);
+- } 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;
+- // Do a simple read with a short timeout
+- read = readSocket(timeout, block);
+- }
+- return read;
+- }
+-
+-
+- // ------------------------------------- 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 (pos >= lastValid) {
+- if (!fill(true,true)) //read body, must be blocking, as the thread is
inside the app
+- return -1;
+- }
+-
+- int length = lastValid - pos;
+- chunk.setBytes(buf, pos, length);
+- pos = lastValid;
+-
+- return (length);
+-
+- }
+-
+-
+- }
+-
+-
+-}
+Index: java/org/apache/coyote/http11/InternalNioOutputBuffer.java
+===================================================================
+--- java/org/apache/coyote/http11/InternalNioOutputBuffer.java (revision 590752)
++++ java/org/apache/coyote/http11/InternalNioOutputBuffer.java (working copy)
+@@ -1,810 +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.nio.ByteBuffer;
+-import java.nio.channels.SelectionKey;
+-import java.nio.channels.Selector;
+-
+-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.net.NioChannel;
+-import org.apache.tomcat.util.net.NioEndpoint;
+-import org.apache.tomcat.util.net.NioSelectorPool;
+-import org.apache.tomcat.util.res.StringManager;
+-
+-/**
+- * Output buffer.
+- *
+- * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+- * @author Filip Hanik
+- */
+-public class InternalNioOutputBuffer
+- implements OutputBuffer {
+-
+-
+- // -------------------------------------------------------------- Constants
+-
+-
+- // ----------------------------------------------------------- Constructors
+- int bbufLimit = 0;
+-
+-
+- /**
+- * Default constructor.
+- */
+- public InternalNioOutputBuffer(Response response) {
+- this(response, Constants.DEFAULT_HTTP_HEADER_BUFFER_SIZE, 10000);
+- }
+-
+-
+- /**
+- * Alternate constructor.
+- */
+- public InternalNioOutputBuffer(Response response, int headerBufferSize, long
writeTimeout) {
+-
+- this.response = response;
+- headers = response.getMimeHeaders();
+-
+- buf = new byte[headerBufferSize];
+-
+- if (headerBufferSize < (8 * 1024)) {
+- bbufLimit = 6 * 1500;
+- } else {
+- bbufLimit = (headerBufferSize / 1500 + 1) * 1500;
+- }
+- //bbuf = ByteBuffer.allocateDirect(bbufLimit);
+-
+- outputStreamOutputBuffer = new SocketOutputBuffer();
+-
+- filterLibrary = new OutputFilter[0];
+- activeFilters = new OutputFilter[0];
+- lastActiveFilter = -1;
+-
+- committed = false;
+- finished = false;
+-
+- this.writeTimeout = writeTimeout;
+-
+- // Cause loading of HttpMessages
+- HttpMessages.getMessage(200);
+-
+- }
+-
+-
+- // -------------------------------------------------------------- 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;
+-
+-
+- /**
+- * Pointer to the current write buffer.
+- */
+- protected byte[] buf;
+-
+-
+- /**
+- * Position in the buffer.
+- */
+- protected int pos;
+-
+-
+- /**
+- * Underlying socket.
+- */
+- protected NioChannel socket;
+-
+- /**
+- * Selector pool, for blocking reads and blocking writes
+- */
+- protected NioSelectorPool pool;
+-
+-
+-
+- /**
+- * 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;
+-
+- /**
+- * Write time out in milliseconds
+- */
+- protected long writeTimeout = -1;
+-
+-
+- // ------------------------------------------------------------- Properties
+-
+-
+- /**
+- * Set the underlying socket.
+- */
+- public void setSocket(NioChannel socket) {
+- this.socket = socket;
+- }
+-
+- public void setWriteTimeout(long writeTimeout) {
+- this.writeTimeout = writeTimeout;
+- }
+-
+- /**
+- * Get the underlying socket input stream.
+- */
+- public NioChannel getSocket() {
+- return socket;
+- }
+-
+- public long getWriteTimeout() {
+- return writeTimeout;
+- }
+-
+- public void setSelectorPool(NioSelectorPool pool) {
+- this.pool = pool;
+- }
+-
+- public NioSelectorPool getSelectorPool() {
+- return pool;
+- }
+- /**
+- * Set the socket buffer size.
+- */
+- public void setSocketBuffer(int socketBufferSize) {
+- // FIXME: Remove
+- }
+-
+-
+- /**
+- * 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
+- flushBuffer();
+-
+- }
+-
+-
+- /**
+- * Reset current response.
+- *
+- * @throws IllegalStateException if the response has already been committed
+- */
+- public void reset() {
+-
+- if (committed)
+- throw new IllegalStateException(/*FIXME:Put an error message*/);
+-
+- // Recycle Request object
+- response.recycle();
+-
+- }
+-
+-
+- /**
+- * Recycle the output buffer. This should be called when closing the
+- * connection.
+- */
+- public void recycle() {
+- // Recycle filters
+- for (int i = 0; i <= lastActiveFilter; i++) {
+- activeFilters[i].recycle();
+- }
+-
+- // Recycle Request object
+- response.recycle();
+- socket.getBufHandler().getWriteBuffer().clear();
+-
+- socket = 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();
+-
+- // 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();
+-
+- flushBuffer();
+-
+- finished = true;
+-
+- }
+-
+-
+- // ------------------------------------------------ HTTP/1.1 Output Methods
+-
+-
+- /**
+- * Send an acknoledgement.
+- */
+- public void sendAck()
+- throws IOException {
+-
+- if (!committed) {
+- //Socket.send(socket, Constants.ACK_BYTES, 0, Constants.ACK_BYTES.length)
< 0
+- socket.getBufHandler()
.getWriteBuffer().put(Constants.ACK_BYTES,0,Constants.ACK_BYTES.length);
+- writeToSocket(socket.getBufHandler() .getWriteBuffer(),true);
+- }
+-
+- }
+-
+- private synchronized void writeToSocket(ByteBuffer bytebuffer, boolean flip) throws
IOException {
+- //int limit = bytebuffer.position();
+- if ( flip ) bytebuffer.flip();
+- int written = 0;
+- Selector selector = null;
+- try {
+- selector = getSelectorPool().get();
+- } catch ( IOException x ) {
+- //ignore
+- }
+- try {
+- written = getSelectorPool().write(bytebuffer, socket, selector,
writeTimeout);
+- //make sure we are flushed
+- do {
+- if (socket.flush(true,selector,writeTimeout)) break;
+- }while ( true );
+- }finally {
+- if ( selector != null ) getSelectorPool().put(selector);
+- }
+- socket.getBufHandler().getWriteBuffer().clear();
+- this.total = 0;
+- }
+-
+-
+- /**
+- * 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 = response.getMessage();
+- if (message == null) {
+- write(HttpMessages.getMessage(status));
+- } else {
+- write(message);
+- }
+-
+- // 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) {
+-
+- 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
+- addToBB(buf, 0, pos);
+- }
+-
+- }
+-
+- int total = 0;
+- private synchronized void addToBB(byte[] buf, int offset, int length) throws
IOException {
+- while (socket.getBufHandler().getWriteBuffer().remaining() < length) {
+- flushBuffer();
+- }
+- socket.getBufHandler().getWriteBuffer().put(buf, offset, length);
+- total += length;
+- NioEndpoint.KeyAttachment ka =
(NioEndpoint.KeyAttachment)socket.getAttachment(false);
+- if ( ka!= null ) ka.access();//prevent timeouts for just doing client writes
+- }
+-
+-
+- /**
+- * 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 = ' ';
+- } else if (c == 127) {
+- 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.
+- */
+- protected void flushBuffer()
+- throws IOException {
+-
+- //prevent timeout for async,
+- SelectionKey key =
socket.getIOChannel().keyFor(socket.getPoller().getSelector());
+- if (key != null) {
+- NioEndpoint.KeyAttachment attach = (NioEndpoint.KeyAttachment)
key.attachment();
+- attach.access();
+- }
+-
+- //write to the socket, if there is anything to write
+- if (socket.getBufHandler().getWriteBuffer().position() > 0) {
+- writeToSocket(socket.getBufHandler().getWriteBuffer(),true);
+- }
+- }
+-
+-
+- // ----------------------------------- 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 {
+-
+- int len = chunk.getLength();
+- int start = chunk.getStart();
+- byte[] b = chunk.getBuffer();
+- while (len > 0) {
+- int thisTime = len;
+- if (socket.getBufHandler().getWriteBuffer().position() ==
socket.getBufHandler().getWriteBuffer().capacity()) {
+- flushBuffer();
+- }
+- if (thisTime > socket.getBufHandler().getWriteBuffer().remaining())
{
+- thisTime = socket.getBufHandler().getWriteBuffer().remaining();
+- }
+- addToBB(b,start,thisTime);
+- len = len - thisTime;
+- start = start + thisTime;
+- }
+- return chunk.getLength();
+-
+- }
+-
+-
+- }
+-
+-
+-}