JBossWeb SVN: r2279 - in branches/7.4.x/src/main/java/org/apache: coyote/http11 and 1 other directory.
by jbossweb-commits@lists.jboss.org
Author: remy.maucherat(a)jboss.com
Date: 2013-10-15 13:30:15 -0400 (Tue, 15 Oct 2013)
New Revision: 2279
Modified:
branches/7.4.x/src/main/java/org/apache/catalina/core/StandardWrapperValve.java
branches/7.4.x/src/main/java/org/apache/coyote/http11/AbstractInternalOutputBuffer.java
branches/7.4.x/src/main/java/org/apache/coyote/http11/Constants.java
branches/7.4.x/src/main/java/org/apache/coyote/http11/Http11NioProcessor.java
branches/7.4.x/src/main/java/org/apache/coyote/http11/Http11NioProtocol.java
branches/7.4.x/src/main/java/org/apache/coyote/http11/InternalNioInputBuffer.java
branches/7.4.x/src/main/java/org/apache/coyote/http11/InternalNioOutputBuffer.java
Log:
- Refactor the non blocking mode of the NIO2 connector.
- Some debug code remains.
Modified: branches/7.4.x/src/main/java/org/apache/catalina/core/StandardWrapperValve.java
===================================================================
--- branches/7.4.x/src/main/java/org/apache/catalina/core/StandardWrapperValve.java 2013-10-14 13:50:24 UTC (rev 2278)
+++ branches/7.4.x/src/main/java/org/apache/catalina/core/StandardWrapperValve.java 2013-10-15 17:30:15 UTC (rev 2279)
@@ -21,6 +21,7 @@
import static org.jboss.web.CatalinaMessages.MESSAGES;
+import java.io.EOFException;
import java.io.IOException;
import java.util.Iterator;
@@ -545,7 +546,7 @@
if (error) {
Throwable throwable = asyncContext.getError();
if (throwable == null) {
- throwable = new Exception();
+ throwable = new EOFException();
}
if (request.getReadListener() != null) {
request.getReadListener().onError(throwable);
Modified: branches/7.4.x/src/main/java/org/apache/coyote/http11/AbstractInternalOutputBuffer.java
===================================================================
--- branches/7.4.x/src/main/java/org/apache/coyote/http11/AbstractInternalOutputBuffer.java 2013-10-14 13:50:24 UTC (rev 2278)
+++ branches/7.4.x/src/main/java/org/apache/coyote/http11/AbstractInternalOutputBuffer.java 2013-10-15 17:30:15 UTC (rev 2279)
@@ -41,76 +41,6 @@
public abstract class AbstractInternalOutputBuffer implements OutputBuffer {
/**
- * Associated Coyote response.
- */
- protected Response response;
-
- /**
- * Headers of the associated request.
- */
- protected MimeHeaders headers;
-
- /**
- * Committed flag.
- */
- protected boolean committed;
-
- /**
- * Finished flag.
- */
- protected boolean finished;
-
- /**
- * Pointer to the current write buffer.
- */
- protected byte[] buf;
-
- /**
- * Position in the buffer.
- */
- protected int pos;
-
- /**
- * Underlying output buffer.
- */
- protected OutputBuffer outputBuffer;
-
- /**
- * Filter library. Note: Filter[0] is always the "chunked" filter.
- */
- protected OutputFilter[] filterLibrary;
-
- /**
- * Active filter (which is actually the top of the pipeline).
- */
- protected OutputFilter[] activeFilters;
-
- /**
- * Index of the last active filter.
- */
- protected int lastActiveFilter;
-
- /**
- * Direct byte buffer used for writing.
- */
- protected ByteBuffer bbuf = null;
-
- /**
- * Leftover bytes which could not be written during a non blocking write.
- */
- protected ByteChunk leftover = null;
-
- /**
- * Non blocking mode.
- */
- protected boolean nonBlocking = false;
-
- /**
- * Write timeout
- */
- protected int writeTimeout = -1;
-
- /**
* Create a new instance of {@code AbstractInternalOutputBuffer}
*
* @param response
@@ -118,553 +48,6 @@
*/
public AbstractInternalOutputBuffer(Response response, int headerBufferSize) {
- this.response = response;
- this.headers = response.getMimeHeaders();
- buf = new byte[headerBufferSize];
- if (headerBufferSize < Constants.DEFAULT_HTTP_HEADER_BUFFER_SIZE) {
- bbuf = ByteBuffer.allocateDirect(6 * 1500);
- } else {
- bbuf = ByteBuffer.allocateDirect((headerBufferSize / 1500 + 1) * 1500);
- }
-
- outputBuffer = new OutputBufferImpl();
- filterLibrary = new OutputFilter[0];
- activeFilters = new OutputFilter[0];
- lastActiveFilter = -1;
-
- committed = false;
- finished = false;
-
- leftover = new ByteChunk();
- nonBlocking = false;
-
- // Cause loading of HttpMessages
- HttpMessages.getMessage(200);
}
- /**
- * Initialize the internal output buffer
- */
- protected abstract void init();
-
- /**
- * Set the non blocking flag.
- *
- * @param nonBlocking
- */
- public void setNonBlocking(boolean nonBlocking) {
- this.nonBlocking = nonBlocking;
- }
-
- /**
- * Get the non blocking flag value.
- *
- * @return non blocking
- */
- public boolean getNonBlocking() {
- return nonBlocking;
- }
-
- /**
- * Add an output filter to the filter library.
- *
- * @param filter
- */
- public void addFilter(OutputFilter filter) {
-
- OutputFilter[] newFilterLibrary = new OutputFilter[filterLibrary.length + 1];
- for (int i = 0; i < filterLibrary.length; i++) {
- newFilterLibrary[i] = filterLibrary[i];
- }
- newFilterLibrary[filterLibrary.length] = filter;
- filterLibrary = newFilterLibrary;
- activeFilters = new OutputFilter[filterLibrary.length];
- }
-
- /**
- * Get filters.
- *
- * @return the list of filters
- */
- public OutputFilter[] getFilters() {
- return filterLibrary;
- }
-
- /**
- * Clear filters.
- */
- public void clearFilters() {
- filterLibrary = new OutputFilter[0];
- lastActiveFilter = -1;
- }
-
- /**
- * Perform a write operation. The operation may be blocking or non-blocking
- * depending on the value of {@code nonBlocking} flag.
- *
- * @param timeout
- * a timeout for the operation
- * @param unit
- * The time unit of the timeout
- * @return
- */
- protected abstract int write(final long timeout, final TimeUnit unit);
-
- /**
- * Add an output filter to the filter library.
- *
- * @param filter
- */
- public void addActiveFilter(OutputFilter filter) {
-
- if (lastActiveFilter == -1) {
- filter.setBuffer(outputBuffer);
- } else {
- for (int i = 0; i <= lastActiveFilter; i++) {
- if (activeFilters[i] == filter)
- return;
- }
- filter.setBuffer(activeFilters[lastActiveFilter]);
- }
-
- activeFilters[++lastActiveFilter] = filter;
- filter.setResponse(response);
- }
-
- public void removeActiveFilters() {
- // Recycle filters
- for (int i = 0; i <= lastActiveFilter; i++) {
- activeFilters[i].recycle();
- }
- lastActiveFilter = -1;
- }
-
- /**
- * Flush the response.
- *
- * @throws IOException
- * an undelying I/O error occured
- */
- public void flush() throws IOException {
- if (!committed) {
-
- // Send the connector a request for commit. The connector should
- // then validate the headers, send them (using sendHeader) and
- // set the filters accordingly.
- response.action(ActionCode.ACTION_COMMIT, null);
- }
-
- // Flush the current buffer
- flushBuffer();
- }
-
- /**
- *
- */
- protected void clearBuffer() {
- synchronized (this.bbuf) {
- this.bbuf.clear();
- }
- }
-
- /**
- * Recycle this object
- */
- public void recycle() {
- // Recycle Request object
- response.recycle();
- this.clearBuffer();
- pos = 0;
- lastActiveFilter = -1;
- committed = false;
- finished = false;
- }
-
- /**
- * End processing of current HTTP request. Note: All bytes of the current
- * request should have been already consumed. This method only resets all
- * the pointers so that we are ready to parse the next HTTP request.
- */
- public void nextRequest() {
- // Recycle Request object
- response.recycle();
-
- // Recycle filters
- for (int i = 0; i <= lastActiveFilter; i++) {
- activeFilters[i].recycle();
- }
-
- // Reset pointers
- leftover.recycle();
- pos = 0;
- lastActiveFilter = -1;
- committed = false;
- finished = false;
- nonBlocking = false;
- }
-
- /**
- * End request.
- *
- * @throws IOException
- * an undelying I/O error occured
- */
- public void endRequest() throws IOException {
-
- if (!committed) {
- // Send the connector a request for commit. The connector should
- // then validate the headers, send them (using sendHeader) and
- // set the filters accordingly.
- response.action(ActionCode.ACTION_COMMIT, null);
- }
-
- if (finished) {
- return;
- }
-
- if (lastActiveFilter != -1) {
- activeFilters[lastActiveFilter].end();
- }
-
- flushBuffer();
- finished = true;
- }
-
- // ------------------------------------------------ HTTP/1.1 Output Methods
-
- /**
- * Send an acknowledgment.
- *
- * @throws Exception
- */
- public abstract void sendAck() throws Exception;
-
- /**
- * Send the response status line.
- */
- public void sendStatus() {
-
- // Write protocol name
- write(Constants.HTTP_11_BYTES);
- buf[pos++] = Constants.SP;
-
- // Write status code
- int status = response.getStatus();
- switch (status) {
- case 200:
- write(Constants._200_BYTES);
- break;
- case 400:
- write(Constants._400_BYTES);
- break;
- case 404:
- write(Constants._404_BYTES);
- break;
- default:
- write(status);
- }
-
- buf[pos++] = Constants.SP;
-
- // Write message
- String message = null;
- if (org.apache.coyote.Constants.USE_CUSTOM_STATUS_MSG_IN_HEADER) {
- message = response.getMessage();
- }
- if (message == null) {
- write(HttpMessages.getMessage(status));
- } else {
- write(message.replace('\n', ' ').replace('\r', ' '));
- }
-
- // End the response status line
- buf[pos++] = Constants.CR;
- buf[pos++] = Constants.LF;
- }
-
- /**
- * Send a header.
- *
- * @param name
- * Header name
- * @param value
- * Header value
- */
- public void sendHeader(MessageBytes name, MessageBytes value) {
- if (name.getLength() > 0 && !value.isNull()) {
- write(name);
- buf[pos++] = Constants.COLON;
- buf[pos++] = Constants.SP;
- write(value);
- buf[pos++] = Constants.CR;
- buf[pos++] = Constants.LF;
- }
- }
-
- /**
- * Send a header.
- *
- * @param name
- * Header name
- * @param value
- * Header value
- */
- public void sendHeader(ByteChunk name, ByteChunk value) {
- write(name);
- buf[pos++] = Constants.COLON;
- buf[pos++] = Constants.SP;
- write(value);
- buf[pos++] = Constants.CR;
- buf[pos++] = Constants.LF;
- }
-
- /**
- * Send a header.
- *
- * @param name
- * Header name
- * @param value
- * Header value
- */
- public void sendHeader(String name, String value) {
- write(name);
- buf[pos++] = Constants.COLON;
- buf[pos++] = Constants.SP;
- write(value);
- buf[pos++] = Constants.CR;
- buf[pos++] = Constants.LF;
- }
-
- /**
- * End the header block.
- */
- public void endHeaders() {
- buf[pos++] = Constants.CR;
- buf[pos++] = Constants.LF;
- }
-
- /**
- * Write the contents of a byte chunk.
- *
- * @param chunk
- * byte chunk
- * @return number of bytes written
- * @throws IOException
- * an undelying I/O error occured
- */
- public abstract int doWrite(ByteChunk chunk, Response res) throws IOException;
-
- /**
- * Commit the response.
- *
- * @throws IOException
- * an undelying I/O error occured
- */
- protected void commit() throws IOException {
-
- // The response is now committed
- committed = true;
- response.setCommitted(true);
-
- if (pos > 0) {
- // Sending the response header buffer
- bbuf.put(buf, 0, pos);
- }
- }
-
- /**
- * This method will write the contents of the specyfied message bytes buffer
- * to the output stream, without filtering. This method is meant to be used
- * to write the response header.
- *
- * @param mb
- * data to be written
- */
- protected void write(MessageBytes mb) {
- if (mb == null) {
- return;
- }
-
- switch (mb.getType()) {
- case MessageBytes.T_BYTES:
- write(mb.getByteChunk());
- break;
- case MessageBytes.T_CHARS:
- write(mb.getCharChunk());
- break;
- default:
- write(mb.toString());
- break;
- }
- }
-
- /**
- * This method will write the contents of the specyfied message bytes buffer
- * to the output stream, without filtering. This method is meant to be used
- * to write the response header.
- *
- * @param bc
- * data to be written
- */
- protected void write(ByteChunk bc) {
- // Writing the byte chunk to the output buffer
- int length = bc.getLength();
- System.arraycopy(bc.getBytes(), bc.getStart(), buf, pos, length);
- pos = pos + length;
- }
-
- /**
- * This method will write the contents of the specyfied char buffer to the
- * output stream, without filtering. This method is meant to be used to
- * write the response header.
- *
- * @param cc
- * data to be written
- */
- protected void write(CharChunk cc) {
- int start = cc.getStart();
- int end = cc.getEnd();
- char[] cbuf = cc.getBuffer();
- for (int i = start; i < end; i++) {
- char c = cbuf[i];
- // Note: This is clearly incorrect for many strings,
- // but is the only consistent approach within the current
- // servlet framework. It must suffice until servlet output
- // streams properly encode their output.
- if (((c <= 31) && (c != 9)) || c == 127 || c > 255) {
- c = ' ';
- }
- buf[pos++] = (byte) c;
- }
-
- }
-
- /**
- * This method will write the contents of the specyfied byte buffer to the
- * output stream, without filtering. This method is meant to be used to
- * write the response header.
- *
- * @param b
- * data to be written
- */
- public void write(byte[] b) {
- // Writing the byte chunk to the output buffer
- System.arraycopy(b, 0, buf, pos, b.length);
- pos = pos + b.length;
- }
-
- /**
- * This method will write the contents of the specyfied String to the output
- * stream, without filtering. This method is meant to be used to write the
- * response header.
- *
- * @param s
- * data to be written
- */
- protected void write(String s) {
- if (s == null) {
- return;
- }
-
- // From the Tomcat 3.3 HTTP/1.0 connector
- int len = s.length();
- for (int i = 0; i < len; i++) {
- char c = s.charAt(i);
- // Note: This is clearly incorrect for many strings,
- // but is the only consistent approach within the current
- // servlet framework. It must suffice until servlet output
- // streams properly encode their output.
- if (((c <= 31) && (c != 9)) || c == 127 || c > 255) {
- c = ' ';
- }
-
- buf[pos++] = (byte) c;
- }
- }
-
- /**
- * This method will print the specified integer to the output stream,
- * without filtering. This method is meant to be used to write the response
- * header.
- *
- * @param i
- * data to be written
- */
- protected void write(int i) {
- write(String.valueOf(i));
- }
-
- /**
- * Callback to write data from the buffer.
- */
- protected abstract void flushBuffer() throws IOException;
-
- /**
- * Flush leftover bytes.
- *
- * @return true if all leftover bytes have been flushed
- * @throws IOException
- */
- public abstract boolean flushLeftover() throws IOException;
-
- // ----------------------------------- OutputBufferImpl Inner Class
-
- /**
- * {@code OutputBufferImpl} This class is an output buffer which will write
- * data to an output stream/channel.
- *
- * Created on Jan 10, 2012 at 12:20:15 PM
- *
- * @author <a href="mailto:nbenothm@redhat.com">Nabil Benothman</a>
- */
- class OutputBufferImpl implements OutputBuffer {
-
- /**
- * Write chunk.
- */
- public int doWrite(ByteChunk chunk, Response res) throws IOException {
- // If non blocking (event) and there are leftover bytes,
- // put all remaining bytes in the leftover buffer (they are
- // part of the same write operation)
- if (leftover.getLength() > 0) {
- leftover.append(chunk);
- return chunk.getLength();
- }
-
- int len = chunk.getLength();
- int start = chunk.getStart();
- byte[] b = chunk.getBuffer();
-
- while (len > 0) {
- int thisTime = len;
- // if (bbuf.position() == bbuf.capacity()) {
- if (!bbuf.hasRemaining()) {
- flushBuffer();
- if (leftover.getLength() > 0) {
- // If non blocking (event) and there are leftover bytes,
- // put all remaining bytes in the leftover buffer (they
- // are
- // part of the same write operation)
- int oldStart = chunk.getOffset();
- chunk.setOffset(start);
- leftover.append(chunk);
- chunk.setOffset(oldStart);
- // After that, all content has been "written"
- return chunk.getLength();
- }
- }
- // if (thisTime > bbuf.capacity() - bbuf.position()) {
- if (thisTime > bbuf.remaining()) {
- // thisTime = bbuf.capacity() - bbuf.position();
- thisTime = bbuf.remaining();
- }
-
- bbuf.put(b, start, thisTime);
- len = len - thisTime;
- start = start + thisTime;
- }
- return chunk.getLength();
- }
- }
-
}
Modified: branches/7.4.x/src/main/java/org/apache/coyote/http11/Constants.java
===================================================================
--- branches/7.4.x/src/main/java/org/apache/coyote/http11/Constants.java 2013-10-14 13:50:24 UTC (rev 2278)
+++ branches/7.4.x/src/main/java/org/apache/coyote/http11/Constants.java 2013-10-15 17:30:15 UTC (rev 2279)
@@ -132,6 +132,13 @@
Integer.valueOf(System.getProperty("org.apache.coyote.http11.DEFAULT_HTTP_HEADER_BUFFER_SIZE", "8192")).intValue();
+ /**
+ * Async buffer size.
+ */
+ public static final int ASYNC_BUFFER_SIZE =
+ Integer.valueOf(System.getProperty("org.apache.coyote.http11.ASYNC_BUFFER_SIZE", "32768")).intValue();
+
+
/* Various constant "strings" */
public static final byte[] CRLF_BYTES = ByteChunk.convertToBytes(CRLF);
public static final byte[] COLON_BYTES = ByteChunk.convertToBytes(": ");
Modified: branches/7.4.x/src/main/java/org/apache/coyote/http11/Http11NioProcessor.java
===================================================================
--- branches/7.4.x/src/main/java/org/apache/coyote/http11/Http11NioProcessor.java 2013-10-14 13:50:24 UTC (rev 2278)
+++ branches/7.4.x/src/main/java/org/apache/coyote/http11/Http11NioProcessor.java 2013-10-15 17:30:15 UTC (rev 2279)
@@ -214,12 +214,6 @@
try {
// If processing a write event, must flush any leftover bytes first
if (status == SocketStatus.OPEN_WRITE) {
- // If the flush does not manage to flush all leftover bytes, the
- // socket should
- // go back to the poller.
- if (!outputBuffer.flushLeftover()) {
- return SocketState.LONG;
- }
// The write notification is now done
writeNotification = false;
// Allow convenient synchronous blocking writes
Modified: branches/7.4.x/src/main/java/org/apache/coyote/http11/Http11NioProtocol.java
===================================================================
--- branches/7.4.x/src/main/java/org/apache/coyote/http11/Http11NioProtocol.java 2013-10-14 13:50:24 UTC (rev 2278)
+++ branches/7.4.x/src/main/java/org/apache/coyote/http11/Http11NioProtocol.java 2013-10-15 17:30:15 UTC (rev 2279)
@@ -806,11 +806,10 @@
* .AsynchronousSocketChannel, org.apache.tomcat.util.net.ChannelStatus)
*/
@Override
- public SocketState event(NioChannel channel, SocketStatus status) {
+ public synchronized SocketState event(NioChannel channel, SocketStatus status) {
Http11NioProcessor processor = connections.get(channel.getId());
SocketState state = SocketState.CLOSED;
-
if (processor != null) {
processor.startProcessing();
// Call the appropriate event
@@ -868,7 +867,7 @@
state = event(channel, SocketStatus.OPEN_READ);
} else if (proto.endpoint.isRunning()) {
proto.endpoint.addEventChannel(channel, processor.getTimeout(),
- processor.getReadNotifications(),
+ false,
processor.getWriteNotification(),
processor.getResumeNotification(), false);
}
Modified: branches/7.4.x/src/main/java/org/apache/coyote/http11/InternalNioInputBuffer.java
===================================================================
--- branches/7.4.x/src/main/java/org/apache/coyote/http11/InternalNioInputBuffer.java 2013-10-14 13:50:24 UTC (rev 2278)
+++ branches/7.4.x/src/main/java/org/apache/coyote/http11/InternalNioInputBuffer.java 2013-10-15 17:30:15 UTC (rev 2279)
@@ -80,11 +80,11 @@
*/
private CompletionHandler<Integer, NioChannel> completionHandler;
- /**
- * Latch to wait for non blocking operations to run the completion handler.
- */
+ /**
+ * Latch used for auto blocking.
+ */
private CountDownLatch latch = null;
-
+
/**
* Create a new instance of {@code InternalNioInputBuffer}
*
@@ -111,36 +111,30 @@
this.completionHandler = new CompletionHandler<Integer, NioChannel>() {
@Override
- public void completed(Integer nBytes, NioChannel attachment) {
+ public synchronized void completed(Integer nBytes, NioChannel attachment) {
if (nBytes < 0) {
failed(new ClosedChannelException(), attachment);
return;
}
- try {
- if (nBytes > 0) {
- bbuf.flip();
- bbuf.get(buf, pos, nBytes);
- lastValid = pos + nBytes;
- if (!processor.isProcessing() && processor.getReadNotifications()) {
- endpoint.processChannel(attachment, SocketStatus.OPEN_READ);
- }
+ if (nBytes > 0) {
+ bbuf.flip();
+ bbuf.get(buf, pos, nBytes);
+ lastValid = pos + nBytes;
+ latch.countDown();
+ if (!processor.isProcessing() && processor.getReadNotifications()) {
+ endpoint.processChannel(attachment, SocketStatus.OPEN_READ);
}
- } finally {
- latch.countDown();
}
}
@Override
public void failed(Throwable exc, NioChannel attachment) {
- try {
- endpoint.removeEventChannel(attachment);
- if (!processor.isProcessing()) {
- endpoint.processChannel(attachment, SocketStatus.ERROR);
- }
- } finally {
- latch.countDown();
- }
+ exc.printStackTrace();
+ endpoint.removeEventChannel(attachment);
+ if (!processor.isProcessing()) {
+ endpoint.processChannel(attachment, SocketStatus.ERROR);
+ }
}
};
}
@@ -420,13 +414,25 @@
private int fill0() throws IOException {
int nRead = 0;
- // Prepare the internal input buffer for reading
- this.prepare();
// Reading from client
if (nonBlocking) {
- nonBlockingRead(bbuf, readTimeout, unit);
- nRead = lastValid - pos;
+ synchronized (completionHandler) {
+ // Prepare the internal input buffer for reading
+ this.prepare();
+ nonBlockingRead(bbuf, readTimeout, unit);
+ nRead = lastValid - pos;
+ }
+ if (nRead == 0 && !available) {
+ try {
+ latch.await(readTimeout, unit);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ nRead = lastValid - pos;
+ }
} else {
+ // Prepare the internal input buffer for reading
+ this.prepare();
nRead = blockingRead(bbuf, readTimeout, unit);
if (nRead > 0) {
bbuf.flip();
@@ -446,7 +452,6 @@
throw new SocketTimeoutException(MESSAGES.failedRead());
}
}
-
return nRead;
}
@@ -485,7 +490,6 @@
try {
latch = new CountDownLatch(1);
ch.read(bb, ch, this.completionHandler);
- latch.await();
} catch (Throwable t) {
if (CoyoteLogger.HTTP_LOGGER.isDebugEnabled()) {
CoyoteLogger.HTTP_LOGGER.errorWithNonBlockingRead(t);
@@ -527,20 +531,28 @@
*/
public int doRead(ByteChunk chunk, Request req) throws IOException {
- if (pos >= lastValid) {
- int nRead = fill0();
- if (nRead < 0) {
- return -1;
- } else if (nRead == 0) {
- return 0;
- }
- }
+ if (pos >= lastValid) {
+ int nRead = fill0();
+ if (nRead < 0) {
+ return -1;
+ } else if (nRead == 0) {
+ return 0;
+ }
+ }
- int length = lastValid - pos;
- chunk.setBytes(buf, pos, length);
- pos = lastValid;
-
- return (length);
+ if (nonBlocking) {
+ synchronized (completionHandler) {
+ int length = lastValid - pos;
+ chunk.setBytes(buf, pos, length);
+ pos = lastValid;
+ return (length);
+ }
+ } else {
+ int length = lastValid - pos;
+ chunk.setBytes(buf, pos, length);
+ pos = lastValid;
+ return (length);
+ }
}
}
}
Modified: branches/7.4.x/src/main/java/org/apache/coyote/http11/InternalNioOutputBuffer.java
===================================================================
--- branches/7.4.x/src/main/java/org/apache/coyote/http11/InternalNioOutputBuffer.java 2013-10-14 13:50:24 UTC (rev 2278)
+++ branches/7.4.x/src/main/java/org/apache/coyote/http11/InternalNioOutputBuffer.java 2013-10-15 17:30:15 UTC (rev 2279)
@@ -1,6 +1,6 @@
/*
* JBoss, Home of Professional Open Source.
- * Copyright 2012 Red Hat, Inc., and individual contributors
+ * Copyright 2013 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,15 +21,19 @@
import static org.jboss.web.CoyoteMessages.MESSAGES;
import java.io.IOException;
-import java.nio.channels.ClosedChannelException;
+import java.nio.ByteBuffer;
import java.nio.channels.CompletionHandler;
-import java.nio.channels.WritePendingException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.apache.coyote.ActionCode;
+import org.apache.coyote.OutputBuffer;
import org.apache.coyote.Response;
import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.CharChunk;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.http.HttpMessages;
+import org.apache.tomcat.util.http.MimeHeaders;
import org.apache.tomcat.util.net.NioChannel;
import org.apache.tomcat.util.net.NioEndpoint;
import org.apache.tomcat.util.net.SocketStatus;
@@ -42,8 +46,78 @@
*
* @author <a href="mailto:nbenothm@redhat.com">Nabil Benothman</a>
*/
-public class InternalNioOutputBuffer extends AbstractInternalOutputBuffer {
+public class InternalNioOutputBuffer implements OutputBuffer {
+ /**
+ * Associated Coyote response.
+ */
+ protected Response response;
+
+ /**
+ * Headers of the associated request.
+ */
+ protected MimeHeaders headers;
+
+ /**
+ * Committed flag.
+ */
+ protected boolean committed;
+
+ /**
+ * Finished flag.
+ */
+ protected boolean finished;
+
+ /**
+ * Pointer to the current write buffer.
+ */
+ protected byte[] buf;
+
+ /**
+ * Position in the buffer.
+ */
+ protected int pos;
+
+ /**
+ * Underlying output buffer.
+ */
+ protected OutputBuffer outputBuffer;
+
+ /**
+ * Filter library. Note: Filter[0] is always the "chunked" filter.
+ */
+ protected OutputFilter[] filterLibrary;
+
+ /**
+ * Active filter (which is actually the top of the pipeline).
+ */
+ protected OutputFilter[] activeFilters;
+
+ /**
+ * Index of the last active filter.
+ */
+ protected int lastActiveFilter;
+
+ /**
+ * Direct byte buffer used for writing.
+ */
+ protected ByteBuffer bbuf = null;
+
+ /**
+ * Leftover bytes which could not be written during a non blocking write.
+ */
+ protected ByteChunk leftover = null;
+
+ /**
+ * Non blocking mode.
+ */
+ protected boolean nonBlocking = false;
+
+ /**
+ * Write timeout
+ */
+ protected int writeTimeout = -1;
+
/**
* Underlying channel.
*/
@@ -64,12 +138,12 @@
*/
private CompletionHandler<Integer, NioChannel> completionHandler;
- /**
- * Latch to wait for non blocking operations to run the completion handler.
- */
- private CountDownLatch latch = null;
-
/**
+ * Latch used for auto blocking.
+ */
+ private CountDownLatch latch = new CountDownLatch(0);
+
+ /**
* Create a new instance of {@code InternalNioOutputBuffer}
*
* @param response
@@ -77,11 +151,35 @@
* @param endpoint
*/
public InternalNioOutputBuffer(Http11NioProcessor processor, Response response, int headerBufferSize, NioEndpoint endpoint) {
- super(response, headerBufferSize);
- this.endpoint = endpoint;
- this.processor = processor;
- // Initialize the input buffer
- this.init();
+
+ this.response = response;
+ this.headers = response.getMimeHeaders();
+ buf = new byte[headerBufferSize];
+ if (headerBufferSize < Constants.DEFAULT_HTTP_HEADER_BUFFER_SIZE) {
+ bbuf = ByteBuffer.allocateDirect(6 * 1500);
+ } else {
+ bbuf = ByteBuffer.allocateDirect((headerBufferSize / 1500 + 1) * 1500);
+ }
+
+ outputBuffer = new OutputBufferImpl();
+ filterLibrary = new OutputFilter[0];
+ activeFilters = new OutputFilter[0];
+ lastActiveFilter = -1;
+
+ committed = false;
+ finished = false;
+
+ leftover = new ByteChunk();
+ nonBlocking = false;
+
+ this.endpoint = endpoint;
+ this.processor = processor;
+ // Initialize the input buffer
+ this.init();
+
+ // Cause loading of HttpMessages
+ HttpMessages.getMessage(200);
+
}
/*
@@ -97,34 +195,34 @@
this.completionHandler = new CompletionHandler<Integer, NioChannel>() {
@Override
- public void completed(Integer nBytes, NioChannel attachment) {
- try {
- if (nBytes < 0) {
- failed(new ClosedChannelException(), attachment);
- return;
- }
-
- if (bbuf.hasRemaining()) {
- try {
- attachment.write(bbuf, writeTimeout, TimeUnit.MILLISECONDS, attachment, this);
- } catch (WritePendingException e) {
- response.setLastWrite(0);
- }
+ public synchronized void completed(Integer nBytes, NioChannel attachment) {
+ if (nBytes < 0) {
+ failed(new IOException(MESSAGES.failedWrite()), attachment);
+ return;
+ }
+ if (!bbuf.hasRemaining()) {
+ bbuf.clear();
+ if (leftover.getLength() > 0) {
+ int n = Math.min(leftover.getLength(), bbuf.remaining());
+ bbuf.put(leftover.getBuffer(), leftover.getOffset(), n).flip();
+ leftover.setOffset(leftover.getOffset() + n);
} else {
- // Clear the buffer when all bytes are written
- clearBuffer();
response.setLastWrite(nBytes);
+ leftover.recycle();
+ latch.countDown();
if (!processor.isProcessing() && processor.getWriteNotification()) {
endpoint.processChannel(attachment, SocketStatus.OPEN_WRITE);
}
+ return;
}
- } finally {
- latch.countDown();
}
+ // Write the remaining bytes
+ attachment.write(bbuf, writeTimeout, TimeUnit.MILLISECONDS, attachment, this);
}
@Override
public void failed(Throwable exc, NioChannel attachment) {
+ exc.printStackTrace();
endpoint.removeEventChannel(attachment);
if (!processor.isProcessing()) {
endpoint.processChannel(attachment, SocketStatus.ERROR);
@@ -152,15 +250,6 @@
}
/**
- * Recycle the output buffer. This should be called when closing the
- * connection.
- */
- public void recycle() {
- super.recycle();
- channel = null;
- }
-
- /**
* Close the channel
*
* @param channel
@@ -209,10 +298,15 @@
*/
private void nonBlockingWrite(final long timeout, final TimeUnit unit) {
try {
+ // Calculate the number of bytes that fit in the buffer
+ int n = Math.min(leftover.getLength(), bbuf.capacity() - bbuf.position());
+ // put bytes in the buffer
+ bbuf.put(leftover.getBuffer(), leftover.getOffset(), n).flip();
+ // Update the offset
+ leftover.setOffset(leftover.getOffset() + n);
+ // Perform the write operation
latch = new CountDownLatch(1);
- // Perform the write operation
- this.channel.write(this.bbuf, timeout, unit, this.channel, this.completionHandler);
- latch.await();
+ this.channel.write(this.bbuf, timeout, unit, this.channel, this.completionHandler);
} catch (Throwable t) {
if (CoyoteLogger.HTTP_LOGGER.isDebugEnabled()) {
CoyoteLogger.HTTP_LOGGER.errorWithNonBlockingWrite(t);
@@ -220,28 +314,25 @@
}
}
- /*
- * (non-Javadoc)
- *
- * @see
- * org.apache.coyote.http11.AbstractInternalOutputBuffer#write(java.nio.
- * ByteBuffer, long, java.util.concurrent.TimeUnit)
- */
- @Override
+ /**
+ * Perform a write operation. The operation may be blocking or non-blocking
+ * depending on the value of {@code nonBlocking} flag.
+ *
+ * @param timeout
+ * a timeout for the operation
+ * @param unit
+ * The time unit of the timeout
+ * @return
+ */
protected int write(final long timeout, final TimeUnit unit) {
- if (nonBlocking) {
- nonBlockingWrite(timeout, unit);
- return 0;
- }
-
return blockingWrite(timeout, unit);
}
- /**
- * Send an acknowledgment.
- *
- * @throws Exception
- */
+ /**
+ * Send an acknowledgment.
+ *
+ * @throws Exception
+ */
public void sendAck() throws Exception {
if (!committed) {
@@ -253,13 +344,15 @@
}
}
- /*
- * (non-Javadoc)
- *
- * @see
- * org.apache.coyote.http11.AbstractInternalOutputBuffer#doWrite(org.apache
- * .tomcat.util.buf.ByteChunk, org.apache.coyote.Response)
- */
+ /**
+ * 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) {
@@ -269,12 +362,6 @@
response.action(ActionCode.ACTION_COMMIT, null);
}
- // If non blocking (event) and there are leftover bytes,
- // and lastWrite was 0 -> error
- if (leftover.getLength() > 0 && !(Http11NioProcessor.containerThread.get() == Boolean.TRUE)) {
- throw new IOException(MESSAGES.invalidBacklog());
- }
-
if (lastActiveFilter == -1) {
return outputBuffer.doWrite(chunk, res);
} else {
@@ -282,66 +369,24 @@
}
}
- /*
- * (non-Javadoc)
- *
- * @see org.apache.coyote.http11.AbstractInternalOutputBuffer#flushBuffer()
- */
+ /**
+ * Callback to write data from the buffer.
+ */
protected void flushBuffer() throws IOException {
int res = 0;
- // If there are still leftover bytes here, this means the user did a
- // direct flush:
- // - If the call is asynchronous, throw an exception
- // - If the call is synchronous, make regular blocking writes to flush
- // the data
- if (leftover.getLength() > 0) {
- if (Http11NioProcessor.containerThread.get() == Boolean.TRUE) {
- // Send leftover bytes
- while (leftover.getLength() > 0) {
- // Calculate the maximum number of bytes that can fit in the
- // buffer
- int n = Math.min(bbuf.capacity() - bbuf.position(), leftover.getLength());
- int off = leftover.getOffset();
- // Put bytes into the buffer
- bbuf.put(leftover.getBuffer(), off, n).flip();
- // Update the offset of the leftover ByteChunck
- leftover.setOffset(off + n);
- while (bbuf.hasRemaining()) {
- res = blockingWrite(writeTimeout, TimeUnit.MILLISECONDS);
- if (res < 0) {
- break;
- }
- }
- bbuf.clear();
- if (res < 0) {
- throw new IOException(MESSAGES.failedWrite());
- }
- }
- leftover.recycle();
- } else {
- throw new IOException(MESSAGES.invalidBacklog());
- }
- }
-
- if (bbuf.position() > 0) {
+ if (!nonBlocking && bbuf.position() > 0) {
bbuf.flip();
- if (nonBlocking) {
- // Perform non blocking writes until all data is written, or the
- // result of the write is 0
- nonBlockingWrite(writeTimeout, TimeUnit.MILLISECONDS);
- } else {
- while (bbuf.hasRemaining()) {
- res = blockingWrite(writeTimeout, TimeUnit.MILLISECONDS);
- if (res <= 0) {
- break;
- }
- }
- response.setLastWrite(res);
- // bbuf.clear();
- clearBuffer();
+ while (bbuf.hasRemaining()) {
+ res = blockingWrite(writeTimeout, TimeUnit.MILLISECONDS);
+ if (res <= 0) {
+ break;
+ }
}
+ response.setLastWrite(res);
+ // bbuf.clear();
+ clearBuffer();
if (res < 0) {
throw new IOException(MESSAGES.failedWrite());
@@ -349,57 +394,478 @@
}
}
- /*
- * (non-Javadoc)
- *
- * @see
- * org.apache.coyote.http11.AbstractInternalOutputBuffer#flushLeftover()
- */
- @Override
- public boolean flushLeftover() throws IOException {
- if (leftover.getLength() == 0) {
- return true;
- }
- // Calculate the number of bytes that fit in the buffer
- int n = Math.min(leftover.getLength(), bbuf.capacity() - bbuf.position());
- // put bytes in the buffer
- bbuf.put(leftover.getBuffer(), leftover.getOffset(), n).flip();
- // Update the offset
- leftover.setOffset(leftover.getOffset() + n);
- final NioChannel ch = channel;
+ /**
+ * Set the non blocking flag.
+ *
+ * @param nonBlocking
+ */
+ public void setNonBlocking(boolean nonBlocking) {
+ this.nonBlocking = nonBlocking;
+ }
- ch.write(bbuf, writeTimeout, TimeUnit.MILLISECONDS, null,
- new CompletionHandler<Integer, Void>() {
+ /**
+ * Get the non blocking flag value.
+ *
+ * @return non blocking
+ */
+ public boolean getNonBlocking() {
+ return nonBlocking;
+ }
- @Override
- public void completed(Integer result, Void attachment) {
- if (result < 0) {
- failed(new IOException(MESSAGES.failedWrite()), attachment);
- return;
- }
- response.setLastWrite(result);
- if (!bbuf.hasRemaining()) {
- bbuf.clear();
- if (leftover.getLength() > 0) {
- int n = Math.min(leftover.getLength(), bbuf.remaining());
- bbuf.put(leftover.getBuffer(), leftover.getOffset(), n).flip();
- leftover.setOffset(leftover.getOffset() + n);
- } else {
- leftover.recycle();
- return;
- }
- }
- // Write the remaining bytes
- ch.write(bbuf, writeTimeout, TimeUnit.MILLISECONDS, null, this);
- }
+ /**
+ * Add an output filter to the filter library.
+ *
+ * @param filter
+ */
+ public void addFilter(OutputFilter filter) {
- @Override
- public void failed(Throwable exc, Void attachment) {
- close(ch);
- }
- });
+ 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];
+ }
- return true;
- }
+ /**
+ * Get filters.
+ *
+ * @return the list of 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.
+ *
+ * @param filter
+ */
+ public void addActiveFilter(OutputFilter filter) {
+
+ if (lastActiveFilter == -1) {
+ filter.setBuffer(outputBuffer);
+ } else {
+ for (int i = 0; i <= lastActiveFilter; i++) {
+ if (activeFilters[i] == filter)
+ return;
+ }
+ filter.setBuffer(activeFilters[lastActiveFilter]);
+ }
+
+ activeFilters[++lastActiveFilter] = filter;
+ filter.setResponse(response);
+ }
+
+ public void removeActiveFilters() {
+ // Recycle filters
+ for (int i = 0; i <= lastActiveFilter; i++) {
+ activeFilters[i].recycle();
+ }
+ lastActiveFilter = -1;
+ }
+
+ /**
+ * Flush the response.
+ *
+ * @throws IOException
+ * an undelying I/O error occured
+ */
+ public void flush() throws IOException {
+ if (!committed) {
+
+ // Send the connector a request for commit. The connector should
+ // then validate the headers, send them (using sendHeader) and
+ // set the filters accordingly.
+ response.action(ActionCode.ACTION_COMMIT, null);
+ }
+
+ // Flush the current buffer
+ flushBuffer();
+ }
+
+ /**
+ *
+ */
+ protected void clearBuffer() {
+ this.bbuf.clear();
+ }
+
+ /**
+ * Recycle this object
+ */
+ public void recycle() {
+ channel = null;
+ // Recycle Request object
+ response.recycle();
+ this.clearBuffer();
+ pos = 0;
+ lastActiveFilter = -1;
+ committed = false;
+ finished = false;
+ }
+
+ /**
+ * End processing of current HTTP request. Note: All bytes of the current
+ * request should have been already consumed. This method only resets all
+ * the pointers so that we are ready to parse the next HTTP request.
+ */
+ public void nextRequest() {
+ // Recycle Request object
+ response.recycle();
+
+ // Recycle filters
+ for (int i = 0; i <= lastActiveFilter; i++) {
+ activeFilters[i].recycle();
+ }
+
+ // Reset pointers
+ leftover.recycle();
+ pos = 0;
+ lastActiveFilter = -1;
+ committed = false;
+ finished = false;
+ nonBlocking = false;
+ }
+
+ /**
+ * End request.
+ *
+ * @throws IOException
+ * an undelying I/O error occured
+ */
+ public void endRequest() throws IOException {
+
+ if (!committed) {
+ // Send the connector a request for commit. The connector should
+ // then validate the headers, send them (using sendHeader) and
+ // set the filters accordingly.
+ response.action(ActionCode.ACTION_COMMIT, null);
+ }
+
+ if (finished) {
+ return;
+ }
+
+ if (lastActiveFilter != -1) {
+ activeFilters[lastActiveFilter].end();
+ }
+
+ flushBuffer();
+ finished = true;
+ }
+
+ // ------------------------------------------------ HTTP/1.1 Output Methods
+
+ /**
+ * Send the response status line.
+ */
+ public void sendStatus() {
+
+ // Write protocol name
+ write(Constants.HTTP_11_BYTES);
+ buf[pos++] = Constants.SP;
+
+ // Write status code
+ int status = response.getStatus();
+ switch (status) {
+ case 200:
+ write(Constants._200_BYTES);
+ break;
+ case 400:
+ write(Constants._400_BYTES);
+ break;
+ case 404:
+ write(Constants._404_BYTES);
+ break;
+ default:
+ write(status);
+ }
+
+ buf[pos++] = Constants.SP;
+
+ // Write message
+ String message = null;
+ if (org.apache.coyote.Constants.USE_CUSTOM_STATUS_MSG_IN_HEADER) {
+ message = response.getMessage();
+ }
+ if (message == null) {
+ write(HttpMessages.getMessage(status));
+ } else {
+ write(message.replace('\n', ' ').replace('\r', ' '));
+ }
+
+ // End the response status line
+ buf[pos++] = Constants.CR;
+ buf[pos++] = Constants.LF;
+ }
+
+ /**
+ * Send a header.
+ *
+ * @param name
+ * Header name
+ * @param value
+ * Header value
+ */
+ public void sendHeader(MessageBytes name, MessageBytes value) {
+ if (name.getLength() > 0 && !value.isNull()) {
+ write(name);
+ buf[pos++] = Constants.COLON;
+ buf[pos++] = Constants.SP;
+ write(value);
+ buf[pos++] = Constants.CR;
+ buf[pos++] = Constants.LF;
+ }
+ }
+
+ /**
+ * Send a header.
+ *
+ * @param name
+ * Header name
+ * @param value
+ * Header value
+ */
+ public void sendHeader(ByteChunk name, ByteChunk value) {
+ write(name);
+ buf[pos++] = Constants.COLON;
+ buf[pos++] = Constants.SP;
+ write(value);
+ buf[pos++] = Constants.CR;
+ buf[pos++] = Constants.LF;
+ }
+
+ /**
+ * Send a header.
+ *
+ * @param name
+ * Header name
+ * @param value
+ * Header value
+ */
+ public void sendHeader(String name, String value) {
+ write(name);
+ buf[pos++] = Constants.COLON;
+ buf[pos++] = Constants.SP;
+ write(value);
+ buf[pos++] = Constants.CR;
+ buf[pos++] = Constants.LF;
+ }
+
+ /**
+ * End the header block.
+ */
+ public void endHeaders() {
+ buf[pos++] = Constants.CR;
+ buf[pos++] = Constants.LF;
+ }
+
+ /**
+ * Commit the response.
+ *
+ * @throws IOException
+ * an undelying I/O error occured
+ */
+ protected void commit() throws IOException {
+
+ // The response is now committed
+ committed = true;
+ response.setCommitted(true);
+
+ if (pos > 0) {
+ // Sending the response header buffer
+ bbuf.put(buf, 0, pos);
+ }
+ }
+
+ /**
+ * This method will write the contents of the specyfied message bytes buffer
+ * to the output stream, without filtering. This method is meant to be used
+ * to write the response header.
+ *
+ * @param mb
+ * data to be written
+ */
+ protected void write(MessageBytes mb) {
+ if (mb == null) {
+ return;
+ }
+
+ switch (mb.getType()) {
+ case MessageBytes.T_BYTES:
+ write(mb.getByteChunk());
+ break;
+ case MessageBytes.T_CHARS:
+ write(mb.getCharChunk());
+ break;
+ default:
+ write(mb.toString());
+ break;
+ }
+ }
+
+ /**
+ * This method will write the contents of the specyfied message bytes buffer
+ * to the output stream, without filtering. This method is meant to be used
+ * to write the response header.
+ *
+ * @param bc
+ * data to be written
+ */
+ protected void write(ByteChunk bc) {
+ // Writing the byte chunk to the output buffer
+ int length = bc.getLength();
+ System.arraycopy(bc.getBytes(), bc.getStart(), buf, pos, length);
+ pos = pos + length;
+ }
+
+ /**
+ * This method will write the contents of the specyfied char buffer to the
+ * output stream, without filtering. This method is meant to be used to
+ * write the response header.
+ *
+ * @param cc
+ * data to be written
+ */
+ protected void write(CharChunk cc) {
+ int start = cc.getStart();
+ int end = cc.getEnd();
+ char[] cbuf = cc.getBuffer();
+ for (int i = start; i < end; i++) {
+ char c = cbuf[i];
+ // Note: This is clearly incorrect for many strings,
+ // but is the only consistent approach within the current
+ // servlet framework. It must suffice until servlet output
+ // streams properly encode their output.
+ if (((c <= 31) && (c != 9)) || c == 127 || c > 255) {
+ c = ' ';
+ }
+ buf[pos++] = (byte) c;
+ }
+
+ }
+
+ /**
+ * This method will write the contents of the specyfied byte buffer to the
+ * output stream, without filtering. This method is meant to be used to
+ * write the response header.
+ *
+ * @param b
+ * data to be written
+ */
+ public void write(byte[] b) {
+ // Writing the byte chunk to the output buffer
+ System.arraycopy(b, 0, buf, pos, b.length);
+ pos = pos + b.length;
+ }
+
+ /**
+ * This method will write the contents of the specyfied String to the output
+ * stream, without filtering. This method is meant to be used to write the
+ * response header.
+ *
+ * @param s
+ * data to be written
+ */
+ protected void write(String s) {
+ if (s == null) {
+ return;
+ }
+
+ // From the Tomcat 3.3 HTTP/1.0 connector
+ int len = s.length();
+ for (int i = 0; i < len; i++) {
+ char c = s.charAt(i);
+ // Note: This is clearly incorrect for many strings,
+ // but is the only consistent approach within the current
+ // servlet framework. It must suffice until servlet output
+ // streams properly encode their output.
+ if (((c <= 31) && (c != 9)) || c == 127 || c > 255) {
+ 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));
+ }
+
+ // ----------------------------------- OutputBufferImpl Inner Class
+
+ /**
+ * {@code OutputBufferImpl} This class is an output buffer which will write
+ * data to an output stream/channel.
+ *
+ * Created on Jan 10, 2012 at 12:20:15 PM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil Benothman</a>
+ */
+ class OutputBufferImpl implements OutputBuffer {
+
+ /**
+ * Write chunk.
+ */
+ public int doWrite(ByteChunk chunk, Response res) throws IOException {
+ if (nonBlocking) {
+ // Autoblocking if the buffer is already full
+ if (leftover.getLength() > Constants.ASYNC_BUFFER_SIZE && response.getFlushLeftovers()
+ && Http11AbstractProcessor.containerThread.get() == Boolean.TRUE) {
+ try {
+ latch.await(writeTimeout, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ // FIXME? throw new IOException(MESSAGES.invalidBacklog());
+ }
+ synchronized (completionHandler) {
+ leftover.append(chunk);
+ if (leftover.getLength() > Constants.ASYNC_BUFFER_SIZE) {
+ response.setLastWrite(0);
+ }
+ if (latch.getCount() == 0) {
+ nonBlockingWrite(writeTimeout, TimeUnit.MILLISECONDS);
+ }
+ }
+ } else {
+ int len = chunk.getLength();
+ int start = chunk.getStart();
+ byte[] b = chunk.getBuffer();
+ while (len > 0) {
+ int thisTime = len;
+ if (!bbuf.hasRemaining()) {
+ flushBuffer();
+ }
+ if (thisTime > bbuf.remaining()) {
+ thisTime = bbuf.remaining();
+ }
+
+ bbuf.put(b, start, thisTime);
+ len = len - thisTime;
+ start = start + thisTime;
+ }
+ }
+ return chunk.getLength();
+ }
+ }
+
}
11 years, 4 months
JBossWeb SVN: r2278 - branches/7.4.x/webapps/docs/config.
by jbossweb-commits@lists.jboss.org
Author: jfrederic.clere(a)jboss.com
Date: 2013-10-14 09:50:24 -0400 (Mon, 14 Oct 2013)
New Revision: 2278
Modified:
branches/7.4.x/webapps/docs/config/jsp.xml
Log:
Arrange the development explainations.
Modified: branches/7.4.x/webapps/docs/config/jsp.xml
===================================================================
--- branches/7.4.x/webapps/docs/config/jsp.xml 2013-10-11 16:18:21 UTC (rev 2277)
+++ branches/7.4.x/webapps/docs/config/jsp.xml 2013-10-14 13:50:24 UTC (rev 2278)
@@ -35,7 +35,7 @@
<attributes>
<attribute name="development" required="true">
- <p>Name of the Virtual Host</p>
+ <p>Enable development mode. (see <code>modification-test-interval</code> too) The default value is <code>false</code>.</p>
</attribute>
<attribute name="disabled" required="true">
11 years, 5 months
JBossWeb SVN: r2277 - branches/7.4.x/src/main/java/org/apache/coyote/http11.
by jbossweb-commits@lists.jboss.org
Author: remy.maucherat(a)jboss.com
Date: 2013-10-11 12:18:21 -0400 (Fri, 11 Oct 2013)
New Revision: 2277
Modified:
branches/7.4.x/src/main/java/org/apache/coyote/http11/Http11NioProcessor.java
branches/7.4.x/src/main/java/org/apache/coyote/http11/Http11NioProtocol.java
branches/7.4.x/src/main/java/org/apache/coyote/http11/InternalNioInputBuffer.java
branches/7.4.x/src/main/java/org/apache/coyote/http11/InternalNioOutputBuffer.java
Log:
Fix some input and output corruption when using non blocking, hopefully without causing regressions.
Modified: branches/7.4.x/src/main/java/org/apache/coyote/http11/Http11NioProcessor.java
===================================================================
--- branches/7.4.x/src/main/java/org/apache/coyote/http11/Http11NioProcessor.java 2013-10-10 16:12:38 UTC (rev 2276)
+++ branches/7.4.x/src/main/java/org/apache/coyote/http11/Http11NioProcessor.java 2013-10-11 16:18:21 UTC (rev 2277)
@@ -96,7 +96,7 @@
this.endpoint = endpoint;
request = new Request();
- inputBuffer = new InternalNioInputBuffer(request, headerBufferSize, endpoint);
+ inputBuffer = new InternalNioInputBuffer(this, request, headerBufferSize, endpoint);
request.setInputBuffer(inputBuffer);
if (endpoint.getUseSendfile()) {
request.setSendfile(true);
@@ -104,7 +104,7 @@
response = new Response();
response.setHook(this);
- outputBuffer = new InternalNioOutputBuffer(response, headerBufferSize, endpoint);
+ outputBuffer = new InternalNioOutputBuffer(this, response, headerBufferSize, endpoint);
response.setOutputBuffer(outputBuffer);
request.setResponse(response);
sslEnabled = endpoint.getSSLEnabled();
@@ -138,6 +138,13 @@
return inputBuffer.available();
}
+ /**
+ * @return true if the processor is not doing anything
+ */
+ public boolean isProcessing() {
+ return processing;
+ }
+
/**
* Add input or output filter.
*
Modified: branches/7.4.x/src/main/java/org/apache/coyote/http11/Http11NioProtocol.java
===================================================================
--- branches/7.4.x/src/main/java/org/apache/coyote/http11/Http11NioProtocol.java 2013-10-10 16:12:38 UTC (rev 2276)
+++ branches/7.4.x/src/main/java/org/apache/coyote/http11/Http11NioProtocol.java 2013-10-11 16:18:21 UTC (rev 2277)
@@ -863,12 +863,15 @@
}
}
} else {
- if (proto.endpoint.isRunning()) {
- proto.endpoint.addEventChannel(channel, processor.getTimeout(),
- processor.getReadNotifications(),
- processor.getWriteNotification(),
- processor.getResumeNotification(), false);
- }
+ if (processor.isAvailable() && processor.getReadNotifications()) {
+ // Call a read event right away
+ state = event(channel, SocketStatus.OPEN_READ);
+ } else if (proto.endpoint.isRunning()) {
+ proto.endpoint.addEventChannel(channel, processor.getTimeout(),
+ processor.getReadNotifications(),
+ processor.getWriteNotification(),
+ processor.getResumeNotification(), false);
+ }
}
processor.endProcessing();
}
Modified: branches/7.4.x/src/main/java/org/apache/coyote/http11/InternalNioInputBuffer.java
===================================================================
--- branches/7.4.x/src/main/java/org/apache/coyote/http11/InternalNioInputBuffer.java 2013-10-10 16:12:38 UTC (rev 2276)
+++ branches/7.4.x/src/main/java/org/apache/coyote/http11/InternalNioInputBuffer.java 2013-10-11 16:18:21 UTC (rev 2277)
@@ -26,6 +26,7 @@
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.CompletionHandler;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.apache.coyote.InputBuffer;
@@ -69,21 +70,32 @@
*/
protected NioEndpoint endpoint = null;
+ /**
+ * NIO processor.
+ */
+ protected Http11NioProcessor processor;
+
/**
* The completion handler used for asynchronous read operations
*/
private CompletionHandler<Integer, NioChannel> completionHandler;
/**
+ * Latch to wait for non blocking operations to run the completion handler.
+ */
+ private CountDownLatch latch = null;
+
+ /**
* Create a new instance of {@code InternalNioInputBuffer}
*
* @param request
* @param headerBufferSize
* @param endpoint
*/
- public InternalNioInputBuffer(Request request, int headerBufferSize, NioEndpoint endpoint) {
+ public InternalNioInputBuffer(Http11NioProcessor processor, Request request, int headerBufferSize, NioEndpoint endpoint) {
super(request, headerBufferSize);
this.endpoint = endpoint;
+ this.processor = processor;
this.init();
}
@@ -100,23 +112,35 @@
@Override
public void completed(Integer nBytes, NioChannel attachment) {
- if (nBytes < 0) {
- failed(new ClosedChannelException(), attachment);
- return;
- }
+ if (nBytes < 0) {
+ failed(new ClosedChannelException(), attachment);
+ return;
+ }
- if (nBytes > 0) {
- bbuf.flip();
- bbuf.get(buf, pos, nBytes);
- lastValid = pos + nBytes;
- endpoint.processChannel(attachment, SocketStatus.OPEN_READ);
- }
+ try {
+ if (nBytes > 0) {
+ bbuf.flip();
+ bbuf.get(buf, pos, nBytes);
+ lastValid = pos + nBytes;
+ if (!processor.isProcessing() && processor.getReadNotifications()) {
+ endpoint.processChannel(attachment, SocketStatus.OPEN_READ);
+ }
+ }
+ } finally {
+ latch.countDown();
+ }
}
@Override
public void failed(Throwable exc, NioChannel attachment) {
- endpoint.removeEventChannel(attachment);
- endpoint.processChannel(attachment, SocketStatus.ERROR);
+ try {
+ endpoint.removeEventChannel(attachment);
+ if (!processor.isProcessing()) {
+ endpoint.processChannel(attachment, SocketStatus.ERROR);
+ }
+ } finally {
+ latch.countDown();
+ }
}
};
}
@@ -391,37 +415,42 @@
* @see org.apache.coyote.http11.AbstractInternalInputBuffer#fill()
*/
protected boolean fill() throws IOException {
- int nRead = 0;
- // Prepare the internal input buffer for reading
- this.prepare();
- // Reading from client
- if (nonBlocking) {
- nonBlockingRead(bbuf, readTimeout, unit);
- } else {
- nRead = blockingRead(bbuf, readTimeout, unit);
- if (nRead > 0) {
- bbuf.flip();
- if (nRead > (buf.length - end)) {
- // An alternative is to bbuf.setLimit(buf.length - end) before the read,
- // which may be less efficient
- buf = new byte[buf.length];
- end = 0;
- pos = end;
- lastValid = pos;
- }
- bbuf.get(buf, pos, nRead);
- lastValid = pos + nRead;
- } else if (nRead == NioChannel.OP_STATUS_CLOSED) {
- throw new IOException(MESSAGES.failedRead());
- } else if (nRead == NioChannel.OP_STATUS_READ_TIMEOUT) {
- throw new SocketTimeoutException(MESSAGES.failedRead());
- }
- }
-
- return (nRead >= 0);
+ return (fill0() >= 0);
}
- /**
+ private int fill0() throws IOException {
+ int nRead = 0;
+ // Prepare the internal input buffer for reading
+ this.prepare();
+ // Reading from client
+ if (nonBlocking) {
+ nonBlockingRead(bbuf, readTimeout, unit);
+ nRead = lastValid - pos;
+ } else {
+ nRead = blockingRead(bbuf, readTimeout, unit);
+ if (nRead > 0) {
+ bbuf.flip();
+ if (nRead > (buf.length - end)) {
+ // An alternative is to bbuf.setLimit(buf.length - end) before the read,
+ // which may be less efficient
+ buf = new byte[buf.length];
+ end = 0;
+ pos = end;
+ lastValid = pos;
+ }
+ bbuf.get(buf, pos, nRead);
+ lastValid = pos + nRead;
+ } else if (nRead == NioChannel.OP_STATUS_CLOSED) {
+ throw new EOFException(MESSAGES.failedRead());
+ } else if (nRead == NioChannel.OP_STATUS_READ_TIMEOUT) {
+ throw new SocketTimeoutException(MESSAGES.failedRead());
+ }
+ }
+
+ return nRead;
+ }
+
+ /**
* Prepare the input buffer for reading
*/
private void prepare() {
@@ -454,7 +483,9 @@
private void nonBlockingRead(final ByteBuffer bb, long timeout, TimeUnit unit) {
final NioChannel ch = this.channel;
try {
+ latch = new CountDownLatch(1);
ch.read(bb, ch, this.completionHandler);
+ latch.await();
} catch (Throwable t) {
if (CoyoteLogger.HTTP_LOGGER.isDebugEnabled()) {
CoyoteLogger.HTTP_LOGGER.errorWithNonBlockingRead(t);
@@ -463,14 +494,6 @@
}
/**
- *
- */
- protected void readAsync() throws IOException {
- this.prepare();
- this.nonBlockingRead(bbuf, readTimeout, unit);
- }
-
- /**
* Read a sequence of bytes in blocking mode from he current channel
*
* @param bb
@@ -505,8 +528,11 @@
public int doRead(ByteChunk chunk, Request req) throws IOException {
if (pos >= lastValid) {
- if (!fill()) {
+ int nRead = fill0();
+ if (nRead < 0) {
return -1;
+ } else if (nRead == 0) {
+ return 0;
}
}
Modified: branches/7.4.x/src/main/java/org/apache/coyote/http11/InternalNioOutputBuffer.java
===================================================================
--- branches/7.4.x/src/main/java/org/apache/coyote/http11/InternalNioOutputBuffer.java 2013-10-10 16:12:38 UTC (rev 2276)
+++ branches/7.4.x/src/main/java/org/apache/coyote/http11/InternalNioOutputBuffer.java 2013-10-11 16:18:21 UTC (rev 2277)
@@ -24,6 +24,7 @@
import java.nio.channels.ClosedChannelException;
import java.nio.channels.CompletionHandler;
import java.nio.channels.WritePendingException;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.apache.coyote.ActionCode;
@@ -31,6 +32,7 @@
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.net.NioChannel;
import org.apache.tomcat.util.net.NioEndpoint;
+import org.apache.tomcat.util.net.SocketStatus;
import org.jboss.web.CoyoteLogger;
/**
@@ -52,11 +54,21 @@
*/
protected NioEndpoint endpoint;
+ /**
+ * NIO processor.
+ */
+ protected Http11NioProcessor processor;
+
/**
* The completion handler used for asynchronous write operations
*/
private CompletionHandler<Integer, NioChannel> completionHandler;
+ /**
+ * Latch to wait for non blocking operations to run the completion handler.
+ */
+ private CountDownLatch latch = null;
+
/**
* Create a new instance of {@code InternalNioOutputBuffer}
*
@@ -64,9 +76,10 @@
* @param headerBufferSize
* @param endpoint
*/
- public InternalNioOutputBuffer(Response response, int headerBufferSize, NioEndpoint endpoint) {
+ public InternalNioOutputBuffer(Http11NioProcessor processor, Response response, int headerBufferSize, NioEndpoint endpoint) {
super(response, headerBufferSize);
this.endpoint = endpoint;
+ this.processor = processor;
// Initialize the input buffer
this.init();
}
@@ -85,29 +98,37 @@
@Override
public void completed(Integer nBytes, NioChannel attachment) {
- if (nBytes < 0) {
- failed(new ClosedChannelException(), attachment);
- return;
- } else {
- response.setLastWrite(nBytes);
- }
+ try {
+ if (nBytes < 0) {
+ failed(new ClosedChannelException(), attachment);
+ return;
+ }
- if (bbuf.hasRemaining()) {
- try {
- attachment.write(bbuf, writeTimeout, TimeUnit.MILLISECONDS, attachment, this);
- } catch (WritePendingException e) {
- response.setLastWrite(0);
- }
- } else {
- // Clear the buffer when all bytes are written
- clearBuffer();
- }
+ if (bbuf.hasRemaining()) {
+ try {
+ attachment.write(bbuf, writeTimeout, TimeUnit.MILLISECONDS, attachment, this);
+ } catch (WritePendingException e) {
+ response.setLastWrite(0);
+ }
+ } else {
+ // Clear the buffer when all bytes are written
+ clearBuffer();
+ response.setLastWrite(nBytes);
+ if (!processor.isProcessing() && processor.getWriteNotification()) {
+ endpoint.processChannel(attachment, SocketStatus.OPEN_WRITE);
+ }
+ }
+ } finally {
+ latch.countDown();
+ }
}
@Override
public void failed(Throwable exc, NioChannel attachment) {
endpoint.removeEventChannel(attachment);
- // endpoint.processChannel(attachment, SocketStatus.ERROR);
+ if (!processor.isProcessing()) {
+ endpoint.processChannel(attachment, SocketStatus.ERROR);
+ }
}
};
}
@@ -188,8 +209,10 @@
*/
private void nonBlockingWrite(final long timeout, final TimeUnit unit) {
try {
+ latch = new CountDownLatch(1);
// Perform the write operation
this.channel.write(this.bbuf, timeout, unit, this.channel, this.completionHandler);
+ latch.await();
} catch (Throwable t) {
if (CoyoteLogger.HTTP_LOGGER.isDebugEnabled()) {
CoyoteLogger.HTTP_LOGGER.errorWithNonBlockingWrite(t);
@@ -334,6 +357,9 @@
*/
@Override
public boolean flushLeftover() throws IOException {
+ if (leftover.getLength() == 0) {
+ return true;
+ }
// Calculate the number of bytes that fit in the buffer
int n = Math.min(leftover.getLength(), bbuf.capacity() - bbuf.position());
// put bytes in the buffer
11 years, 5 months
JBossWeb SVN: r2276 - in branches/7.4.x/src/main/java/org/apache/tomcat/websocket: server and 1 other directory.
by jbossweb-commits@lists.jboss.org
Author: remy.maucherat(a)jboss.com
Date: 2013-10-10 12:12:38 -0400 (Thu, 10 Oct 2013)
New Revision: 2276
Modified:
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/WsRemoteEndpointImplBase.java
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/WsRemoteEndpointImplClient.java
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/WsSession.java
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/server/WsRemoteEndpointImplServer.java
Log:
Port patch and improve my flush trick (don't know if it is correct yet).
Modified: branches/7.4.x/src/main/java/org/apache/tomcat/websocket/WsRemoteEndpointImplBase.java
===================================================================
--- branches/7.4.x/src/main/java/org/apache/tomcat/websocket/WsRemoteEndpointImplBase.java 2013-10-09 06:59:38 UTC (rev 2275)
+++ branches/7.4.x/src/main/java/org/apache/tomcat/websocket/WsRemoteEndpointImplBase.java 2013-10-10 16:12:38 UTC (rev 2276)
@@ -274,7 +274,11 @@
MessagePart mpNext = messagePartQueue.poll();
if (mpNext == null) {
messagePartInProgress = false;
- } else {
+ } else if (!closed){
+ // Session may have been closed unexpectedly in the middle of
+ // sending a fragmented message closing the endpoint. If this
+ // happens, clearly there is no point trying to send the rest of
+ // the message.
writeMessagePart(mpNext);
}
}
@@ -561,6 +565,7 @@
protected abstract void doWrite(SendHandler handler, ByteBuffer... data);
+ protected abstract void doFlush() throws IOException;
protected abstract boolean isMasked();
protected abstract void doClose();
Modified: branches/7.4.x/src/main/java/org/apache/tomcat/websocket/WsRemoteEndpointImplClient.java
===================================================================
--- branches/7.4.x/src/main/java/org/apache/tomcat/websocket/WsRemoteEndpointImplClient.java 2013-10-09 06:59:38 UTC (rev 2275)
+++ branches/7.4.x/src/main/java/org/apache/tomcat/websocket/WsRemoteEndpointImplClient.java 2013-10-10 16:12:38 UTC (rev 2276)
@@ -16,6 +16,7 @@
*/
package org.apache.tomcat.websocket;
+import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.TimeUnit;
@@ -37,6 +38,11 @@
@Override
+ protected void doFlush() throws IOException {
+ }
+
+
+ @Override
protected void doWrite(SendHandler handler, ByteBuffer... data) {
long timeout = getSendTimeout();
if (timeout < 1) {
Modified: branches/7.4.x/src/main/java/org/apache/tomcat/websocket/WsSession.java
===================================================================
--- branches/7.4.x/src/main/java/org/apache/tomcat/websocket/WsSession.java 2013-10-09 06:59:38 UTC (rev 2275)
+++ branches/7.4.x/src/main/java/org/apache/tomcat/websocket/WsSession.java 2013-10-10 16:12:38 UTC (rev 2276)
@@ -440,7 +440,7 @@
* Force an autoblocking flush.
*/
public void forceFlush() throws IOException {
- wsRemoteEndpoint.flushBatch();
+ wsRemoteEndpoint.doFlush();
}
private void fireEndpointOnClose(CloseReason closeReason) {
Modified: branches/7.4.x/src/main/java/org/apache/tomcat/websocket/server/WsRemoteEndpointImplServer.java
===================================================================
--- branches/7.4.x/src/main/java/org/apache/tomcat/websocket/server/WsRemoteEndpointImplServer.java 2013-10-09 06:59:38 UTC (rev 2275)
+++ branches/7.4.x/src/main/java/org/apache/tomcat/websocket/server/WsRemoteEndpointImplServer.java 2013-10-10 16:12:38 UTC (rev 2276)
@@ -65,6 +65,14 @@
}
+ @Override
+ protected void doFlush() throws IOException {
+ if (timeoutExpiry != -1) {
+ sos.flush();
+ }
+ }
+
+
public void onWritePossible() {
boolean complete = true;
try {
@@ -81,8 +89,11 @@
}
}
if (complete) {
+ timeoutExpiry = -1;
wsWriteTimeout.unregister(this);
clearHandler(null);
+ // Explicit flush for compatibility with buffered streams
+ sos.flush();
if (close) {
close();
}
11 years, 5 months
JBossWeb SVN: r2275 - in branches/7.4.x/src/main/java/org/apache/tomcat/websocket: server and 1 other directory.
by jbossweb-commits@lists.jboss.org
Author: remy.maucherat(a)jboss.com
Date: 2013-10-09 02:59:38 -0400 (Wed, 09 Oct 2013)
New Revision: 2275
Modified:
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/WsFrameBase.java
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/server/WsRemoteEndpointImplServer.java
Log:
Port ws logic fixes.
Modified: branches/7.4.x/src/main/java/org/apache/tomcat/websocket/WsFrameBase.java
===================================================================
--- branches/7.4.x/src/main/java/org/apache/tomcat/websocket/WsFrameBase.java 2013-10-03 14:00:02 UTC (rev 2274)
+++ branches/7.4.x/src/main/java/org/apache/tomcat/websocket/WsFrameBase.java 2013-10-09 06:59:38 UTC (rev 2275)
@@ -261,14 +261,16 @@
private boolean processData() throws IOException {
- checkRoomPayload();
+ boolean result;
if (Util.isControl(opCode)) {
- return processDataControl();
+ result = processDataControl();
} else if (textMessage) {
- return processDataText();
+ result = processDataText();
} else {
- return processDataBinary();
+ result = processDataBinary();
}
+ checkRoomPayload();
+ return result;
}
Modified: branches/7.4.x/src/main/java/org/apache/tomcat/websocket/server/WsRemoteEndpointImplServer.java
===================================================================
--- branches/7.4.x/src/main/java/org/apache/tomcat/websocket/server/WsRemoteEndpointImplServer.java 2013-10-03 14:00:02 UTC (rev 2274)
+++ branches/7.4.x/src/main/java/org/apache/tomcat/websocket/server/WsRemoteEndpointImplServer.java 2013-10-09 06:59:38 UTC (rev 2275)
@@ -82,25 +82,18 @@
}
if (complete) {
wsWriteTimeout.unregister(this);
- // Explicit flush for compatibility with buffered streams
- sos.flush();
+ clearHandler(null);
if (close) {
close();
}
- // Setting the result marks this (partial) message as
- // complete which means the next one may be sent which
- // could update the value of the handler. Therefore, keep a
- // local copy before signalling the end of the (partial)
- // message.
- clearHandler(null);
break;
}
}
} catch (IOException ioe) {
wsWriteTimeout.unregister(this);
- close();
clearHandler(ioe);
+ close();
}
if (!complete) {
// Async write is in progress
@@ -143,6 +136,11 @@
private void clearHandler(Throwable t) {
+ // Setting the result marks this (partial) message as
+ // complete which means the next one may be sent which
+ // could update the value of the handler. Therefore, keep a
+ // local copy before signalling the end of the (partial)
+ // message.
SendHandler sh = handler;
handler = null;
if (sh != null) {
11 years, 5 months
JBossWeb SVN: r2274 - branches/7.4.x/src/main/java/org/apache/tomcat/websocket.
by jbossweb-commits@lists.jboss.org
Author: remy.maucherat(a)jboss.com
Date: 2013-10-03 10:00:02 -0400 (Thu, 03 Oct 2013)
New Revision: 2274
Modified:
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/WsFrameClient.java
Log:
Port WS patch.
Modified: branches/7.4.x/src/main/java/org/apache/tomcat/websocket/WsFrameClient.java
===================================================================
--- branches/7.4.x/src/main/java/org/apache/tomcat/websocket/WsFrameClient.java 2013-10-02 11:19:56 UTC (rev 2273)
+++ branches/7.4.x/src/main/java/org/apache/tomcat/websocket/WsFrameClient.java 2013-10-03 14:00:02 UTC (rev 2274)
@@ -102,7 +102,14 @@
try {
processSocketRead();
} catch (IOException e) {
- close(e);
+ // Only send a close message on an IOException if the client
+ // has not yet received a close control message from the server
+ // as the IOException may be in response to the client
+ // continuing to send a message after the server sent a close
+ // control message.
+ if (isOpen()) {
+ close(e);
+ }
}
}
11 years, 5 months
JBossWeb SVN: r2273 - branches/7.4.x/src/main/java/org/apache/tomcat/websocket/server.
by jbossweb-commits@lists.jboss.org
Author: remy.maucherat(a)jboss.com
Date: 2013-10-02 07:19:56 -0400 (Wed, 02 Oct 2013)
New Revision: 2273
Modified:
branches/7.4.x/src/main/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java
Log:
Port websockets patch.
Modified: branches/7.4.x/src/main/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java
===================================================================
--- branches/7.4.x/src/main/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java 2013-10-01 15:11:54 UTC (rev 2272)
+++ branches/7.4.x/src/main/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java 2013-10-02 11:19:56 UTC (rev 2273)
@@ -192,6 +192,9 @@
wsProtocolHandler.close(cr);
} catch (IOException ioe) {
onError(ioe);
+ CloseReason cr = new CloseReason(
+ CloseCodes.CLOSED_ABNORMALLY, ioe.getMessage());
+ wsProtocolHandler.close(cr);
}
}
11 years, 5 months
JBossWeb SVN: r2272 - branches/7.4.x/webapps/docs.
by jbossweb-commits@lists.jboss.org
Author: remy.maucherat(a)jboss.com
Date: 2013-10-01 11:11:54 -0400 (Tue, 01 Oct 2013)
New Revision: 2272
Modified:
branches/7.4.x/webapps/docs/aio.xml
Log:
Fix the doc on IO, for sendfile and mention websockets features.
Modified: branches/7.4.x/webapps/docs/aio.xml
===================================================================
--- branches/7.4.x/webapps/docs/aio.xml 2013-10-01 10:31:15 UTC (rev 2271)
+++ branches/7.4.x/webapps/docs/aio.xml 2013-10-01 15:11:54 UTC (rev 2272)
@@ -406,29 +406,49 @@
asynchronously in the most efficient way. Instead of sending a large response using
blocking writes, it is possible to write content to a static file, and write it
using a sendfile code. A caching valve could take advantage of this to cache the
- response data in a file rather than store it in memory. Sendfile support is
- available if the request attribute <code>org.apache.tomcat.sendfile.support</code>
- is set to <code>Boolean.TRUE</code>.
+ response data in a file rather than store it in memory.
</p>
<p>
Any servlet can instruct JBoss Web to perform a sendfile call by setting the appropriate
response attributes. It is also necessary to correctly set the content length
- for the response. When using sendfile, it is best to ensure that neither the
+ for the response. When using sendfile, it is usually necessary to ensure that neither the
request or response have been wrapped, since as the response body will be sent later
- by the connector itself, it cannot be filtered. Other than setting the 3 needed
- request attributes, the servlet should not send any response data, but it may use
- any method which will result in modifying the response header (like setting cookies).
+ by the connector itself, it cannot be filtered. The servlet should not send any response data,
+ but it may use any method which will result in modifying the response header (like setting cookies).
</p>
- <ul>
- <li>org.apache.tomcat.sendfile.filename: Canonical filename of the file which will be sent as
- a String</li>
- <li>org.apache.tomcat.sendfile.start: Start offset as a Long</li>
- <li>org.apache.tomcat.sendfile.end: End offset as a Long</li>
- </ul>
+ <p>
+ The Servlet should first check if the request object can be cast to the internal
+ org.apache.catalina.connector.RequestFacade type, then use the hasSendfile() method to
+ determine if the connector used supports sendfile and has it enabled. The file content is then
+ sent using the sendFile(String path, String absolutePath, long start, long end) method on the
+ response, after casting it to the org.apache.catalina.connector.ResponseFacade type.
+ Path is the pathname of the file relative to the webapp root if is located inside it,
+ absolutePath allows sending any other file from the system (path should in this case be set
+ to null). Start and end specify the start and end offset of the data that should be sent.
+ </p>
</section>
+ <section name="Tomcat Websockets API">
+
+ <p>
+ JBoss Web supports the Apache Tomcat 7 Websockets API which provides a ready to use
+ toolkit for building simple websockets enabled web applications.
+ </p>
+
+ </section>
+
+ <section name="Websockets 1.0 API">
+
+ <p>
+ JBoss Web supports the Websockets 1.0 API specification included in Java EE 7. To enable it
+ in your application, add a JAR declaring the org.apache.tomcat.websocket.server.WsSci
+ Servlet container initializer.
+ </p>
+
+ </section>
+
</body>
</document>
11 years, 5 months
JBossWeb SVN: r2271 - in branches/JBOSSWEB_7_2_0_FINAL_BZ-1013996/src/main/java/org/apache/tomcat/util/http/fileupload: disk and 1 other directory.
by jbossweb-commits@lists.jboss.org
Author: mmusaji
Date: 2013-10-01 06:31:15 -0400 (Tue, 01 Oct 2013)
New Revision: 2271
Modified:
branches/JBOSSWEB_7_2_0_FINAL_BZ-1013996/src/main/java/org/apache/tomcat/util/http/fileupload/FileItem.java
branches/JBOSSWEB_7_2_0_FINAL_BZ-1013996/src/main/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItem.java
Log:
Merged BZ-968578 and BZ-974813 (CVE-2013-2185)
Modified: branches/JBOSSWEB_7_2_0_FINAL_BZ-1013996/src/main/java/org/apache/tomcat/util/http/fileupload/FileItem.java
===================================================================
--- branches/JBOSSWEB_7_2_0_FINAL_BZ-1013996/src/main/java/org/apache/tomcat/util/http/fileupload/FileItem.java 2013-10-01 09:56:17 UTC (rev 2270)
+++ branches/JBOSSWEB_7_2_0_FINAL_BZ-1013996/src/main/java/org/apache/tomcat/util/http/fileupload/FileItem.java 2013-10-01 10:31:15 UTC (rev 2271)
@@ -20,7 +20,6 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.io.Serializable;
import java.io.UnsupportedEncodingException;
/**
@@ -50,7 +49,7 @@
*
* @version $Id: FileItem.java 1533 2010-08-18 10:01:08Z remy.maucherat(a)jboss.com $
*/
-public interface FileItem extends Serializable {
+public interface FileItem extends FileItemHeadersSupport {
// ------------------------------- Methods from javax.activation.DataSource
Modified: branches/JBOSSWEB_7_2_0_FINAL_BZ-1013996/src/main/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItem.java
===================================================================
--- branches/JBOSSWEB_7_2_0_FINAL_BZ-1013996/src/main/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItem.java 2013-10-01 09:56:17 UTC (rev 2270)
+++ branches/JBOSSWEB_7_2_0_FINAL_BZ-1013996/src/main/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItem.java 2013-10-01 10:31:15 UTC (rev 2271)
@@ -26,8 +26,6 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.Map;
@@ -89,12 +87,6 @@
// ----------------------------------------------------- Manifest constants
/**
- * The UID to use when serializing this instance.
- */
- private static final long serialVersionUID = 2237570099615271025L;
-
-
- /**
* Default content charset to be used when no explicit charset
* parameter is provided by the sender. Media subtypes of the
* "text" type are defined to have a default charset value of
@@ -180,11 +172,6 @@
private transient File tempFile;
/**
- * File to allow for serialization of the content of this item.
- */
- private File dfosFile;
-
- /**
* The file items headers.
*/
private FileItemHeaders headers;
@@ -658,9 +645,6 @@
return id;
}
-
-
-
/**
* Returns a string representation of this object.
*
@@ -679,58 +663,7 @@
+ this.getFieldName();
}
-
- // -------------------------------------------------- Serialization methods
-
-
/**
- * Writes the state of this object during serialization.
- *
- * @param out The stream to which the state should be written.
- *
- * @throws IOException if an error occurs.
- */
- private void writeObject(ObjectOutputStream out) throws IOException {
- // Read the data
- if (dfos.isInMemory()) {
- cachedContent = get();
- } else {
- cachedContent = null;
- dfosFile = dfos.getFile();
- }
-
- // write out values
- out.defaultWriteObject();
- }
-
- /**
- * Reads the state of this object during deserialization.
- *
- * @param in The stream from which the state should be read.
- *
- * @throws IOException if an error occurs.
- * @throws ClassNotFoundException if class cannot be found.
- */
- private void readObject(ObjectInputStream in)
- throws IOException, ClassNotFoundException {
- // read values
- in.defaultReadObject();
-
- OutputStream output = getOutputStream();
- if (cachedContent != null) {
- output.write(cachedContent);
- } else {
- FileInputStream input = new FileInputStream(dfosFile);
- IOUtils.copy(input, output);
- dfosFile.delete();
- dfosFile = null;
- }
- output.close();
-
- cachedContent = null;
- }
-
- /**
* Returns the file item headers.
* @return The file items headers.
*/
11 years, 5 months
JBossWeb SVN: r2270 - branches.
by jbossweb-commits@lists.jboss.org
Author: mmusaji
Date: 2013-10-01 05:56:17 -0400 (Tue, 01 Oct 2013)
New Revision: 2270
Added:
branches/JBOSSWEB_7_2_2_FINAL_BZ-1005130/
Removed:
branches/JBOSSWEB_7_2_2_FINAL_BZ-1013996/
Log:
I renamed the wrong branch so I'm putting it back
11 years, 5 months