Author: violetagg Date: Mon Aug 29 16:31:46 2016 New Revision: 1758257 URL: http://svn.apache.org/viewvc?rev=1758257&view=rev Log: Introduce a new method org.apache.coyote.OutputBuffer.doWrite(ByteBuffer)
Modified: tomcat/trunk/java/org/apache/coyote/OutputBuffer.java tomcat/trunk/java/org/apache/coyote/ajp/AjpMessage.java tomcat/trunk/java/org/apache/coyote/ajp/AjpProcessor.java tomcat/trunk/java/org/apache/coyote/http11/Http11OutputBuffer.java tomcat/trunk/java/org/apache/coyote/http11/filters/ChunkedOutputFilter.java tomcat/trunk/java/org/apache/coyote/http11/filters/GzipOutputFilter.java tomcat/trunk/java/org/apache/coyote/http11/filters/IdentityOutputFilter.java tomcat/trunk/java/org/apache/coyote/http11/filters/VoidOutputFilter.java tomcat/trunk/java/org/apache/coyote/http2/Stream.java tomcat/trunk/test/org/apache/coyote/http11/filters/TesterOutputBuffer.java Modified: tomcat/trunk/java/org/apache/coyote/OutputBuffer.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/OutputBuffer.java?rev=1758257&r1=1758256&r2=1758257&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/OutputBuffer.java (original) +++ tomcat/trunk/java/org/apache/coyote/OutputBuffer.java Mon Aug 29 16:31:46 2016 @@ -17,6 +17,7 @@ package org.apache.coyote; import java.io.IOException; +import java.nio.ByteBuffer; import org.apache.tomcat.util.buf.ByteChunk; @@ -24,7 +25,7 @@ import org.apache.tomcat.util.buf.ByteCh * Output buffer. * * This class is used internally by the protocol implementation. All writes from - * higher level code should happen via Resonse.doWrite(). + * higher level code should happen via Response.doWrite(). * * @author Remy Maucherat */ @@ -44,6 +45,19 @@ public interface OutputBuffer { /** + * Write the given data to the response. The caller owns the chunks. + * + * @param chunk data to write + * + * @return The number of bytes written which may be less than available in + * the input chunk + * + * @throws IOException an underlying I/O error occurred + */ + public int doWrite(ByteBuffer chunk) throws IOException; + + + /** * Bytes written to the underlying socket. This includes the effects of * chunking, compression, etc. * Modified: tomcat/trunk/java/org/apache/coyote/ajp/AjpMessage.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/ajp/AjpMessage.java?rev=1758257&r1=1758256&r2=1758257&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/ajp/AjpMessage.java (original) +++ tomcat/trunk/java/org/apache/coyote/ajp/AjpMessage.java Mon Aug 29 16:31:46 2016 @@ -17,6 +17,8 @@ package org.apache.coyote.ajp; +import java.nio.ByteBuffer; + import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.buf.ByteChunk; @@ -220,18 +222,47 @@ public class AjpMessage { * @param numBytes The number of bytes to copy. */ public void appendBytes(byte[] b, int off, int numBytes) { + if (checkOverflow(numBytes)) { + return; + } + appendInt(numBytes); + System.arraycopy(b, off, buf, pos, numBytes); + pos += numBytes; + appendByte(0); + } + + + /** + * Copy a chunk of bytes into the packet, starting at the current + * write position. The chunk of bytes is encoded with the length + * in two bytes first, then the data itself, and finally a + * terminating \0 (which is <B>not</B> included in the encoded + * length). + * + * @param b The ByteBuffer from which to copy bytes. + */ + public void appendBytes(ByteBuffer b) { + int numBytes = b.remaining(); + if (checkOverflow(numBytes)) { + return; + } + appendInt(numBytes); + b.get(buf, pos, numBytes); + pos += numBytes; + appendByte(0); + } + + + private boolean checkOverflow(int numBytes) { if (pos + numBytes + 3 > buf.length) { log.error(sm.getString("ajpmessage.overflow", "" + numBytes, "" + pos), new ArrayIndexOutOfBoundsException()); if (log.isDebugEnabled()) { dump("Overflow/coBytes"); } - return; + return true; } - appendInt(numBytes); - System.arraycopy(b, off, buf, pos, numBytes); - pos += numBytes; - appendByte(0); + return false; } Modified: tomcat/trunk/java/org/apache/coyote/ajp/AjpProcessor.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/ajp/AjpProcessor.java?rev=1758257&r1=1758256&r2=1758257&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/ajp/AjpProcessor.java (original) +++ tomcat/trunk/java/org/apache/coyote/ajp/AjpProcessor.java Mon Aug 29 16:31:46 2016 @@ -21,6 +21,7 @@ import java.io.EOFException; import java.io.IOException; import java.io.InterruptedIOException; import java.net.InetAddress; +import java.nio.ByteBuffer; import java.security.NoSuchProviderException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; @@ -1313,10 +1314,8 @@ public class AjpProcessor extends Abstra // Write this chunk while (len > 0) { - int thisTime = len; - if (thisTime > outputMaxChunkSize) { - thisTime = outputMaxChunkSize; - } + int thisTime = Math.min(len, outputMaxChunkSize); + responseMessage.reset(); responseMessage.appendByte(Constants.JK_AJP13_SEND_BODY_CHUNK); responseMessage.appendBytes(chunk.getBytes(), chunk.getOffset() + off, thisTime); @@ -1332,6 +1331,32 @@ public class AjpProcessor extends Abstra } + private void writeData(ByteBuffer chunk) throws IOException { + boolean blocking = (response.getWriteListener() == null); + + int len = chunk.remaining(); + int off = 0; + + // Write this chunk + while (len > 0) { + int thisTime = Math.min(len, outputMaxChunkSize); + + responseMessage.reset(); + responseMessage.appendByte(Constants.JK_AJP13_SEND_BODY_CHUNK); + chunk.limit(chunk.position() + thisTime); + responseMessage.appendBytes(chunk); + responseMessage.end(); + socketWrapper.write(blocking, responseMessage.getBuffer(), 0, responseMessage.getLen()); + socketWrapper.flush(blocking); + + len -= thisTime; + off += thisTime; + } + + bytesWritten += off; + } + + private boolean hasDataToWrite() { return responseMsgPos != -1 || socketWrapper.hasDataToWrite(); } @@ -1397,6 +1422,27 @@ public class AjpProcessor extends Abstra } @Override + public int doWrite(ByteBuffer chunk) throws IOException { + + if (!response.isCommitted()) { + // Validate and write response headers + try { + prepareResponse(); + } catch (IOException e) { + setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e); + } + } + + int len = 0; + if (!swallowResponse) { + len = chunk.remaining(); + writeData(chunk); + len -= chunk.remaining(); + } + return len; + } + + @Override public long getBytesWritten() { return bytesWritten; } Modified: tomcat/trunk/java/org/apache/coyote/http11/Http11OutputBuffer.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/Http11OutputBuffer.java?rev=1758257&r1=1758256&r2=1758257&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http11/Http11OutputBuffer.java (original) +++ tomcat/trunk/java/org/apache/coyote/http11/Http11OutputBuffer.java Mon Aug 29 16:31:46 2016 @@ -17,6 +17,7 @@ package org.apache.coyote.http11; import java.io.IOException; +import java.nio.ByteBuffer; import org.apache.coyote.ActionCode; import org.apache.coyote.OutputBuffer; @@ -209,6 +210,24 @@ public class Http11OutputBuffer implemen @Override + public int doWrite(ByteBuffer chunk) throws IOException { + + if (!response.isCommitted()) { + // 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.COMMIT, null); + } + + if (lastActiveFilter == -1) { + return outputStreamOutputBuffer.doWrite(chunk); + } else { + return activeFilters[lastActiveFilter].doWrite(chunk); + } + } + + + @Override public long getBytesWritten() { if (lastActiveFilter == -1) { return outputStreamOutputBuffer.getBytesWritten(); @@ -562,6 +581,18 @@ public class Http11OutputBuffer implemen byteCount += len; return len; } + + /** + * Write chunk. + */ + @Override + public int doWrite(ByteBuffer chunk) throws IOException { + int len = chunk.remaining(); + socketWrapper.write(isBlocking(), chunk); + len -= chunk.remaining(); + byteCount += len; + return len; + } @Override public long getBytesWritten() { Modified: tomcat/trunk/java/org/apache/coyote/http11/filters/ChunkedOutputFilter.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/filters/ChunkedOutputFilter.java?rev=1758257&r1=1758256&r2=1758257&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http11/filters/ChunkedOutputFilter.java (original) +++ tomcat/trunk/java/org/apache/coyote/http11/filters/ChunkedOutputFilter.java Mon Aug 29 16:31:46 2016 @@ -18,6 +18,7 @@ package org.apache.coyote.http11.filters; import java.io.IOException; +import java.nio.ByteBuffer; import org.apache.coyote.OutputBuffer; import org.apache.coyote.Response; @@ -89,8 +90,7 @@ public class ChunkedOutputFilter impleme // --------------------------------------------------- OutputBuffer Methods @Override - public int doWrite(ByteChunk chunk) - throws IOException { + public int doWrite(ByteChunk chunk) throws IOException { int result = chunk.getLength(); @@ -98,14 +98,32 @@ public class ChunkedOutputFilter impleme return 0; } - // Calculate chunk header - int pos = 7; - int current = result; - while (current > 0) { - int digit = current % 16; - current = current / 16; - chunkLength[pos--] = HexUtils.getHex(digit); + int pos = calculateChunkHeader(result); + + chunkHeader.setBytes(chunkLength, pos + 1, 9 - pos); + buffer.doWrite(chunkHeader); + + buffer.doWrite(chunk); + + chunkHeader.setBytes(chunkLength, 8, 2); + buffer.doWrite(chunkHeader); + + return result; + + } + + + @Override + public int doWrite(ByteBuffer chunk) throws IOException { + + int result = chunk.remaining(); + + if (result <= 0) { + return 0; } + + int pos = calculateChunkHeader(result); + chunkHeader.setBytes(chunkLength, pos + 1, 9 - pos); buffer.doWrite(chunkHeader); @@ -119,6 +137,19 @@ public class ChunkedOutputFilter impleme } + private int calculateChunkHeader(int len) { + // Calculate chunk header + int pos = 7; + int current = len; + while (current > 0) { + int digit = current % 16; + current = current / 16; + chunkLength[pos--] = HexUtils.getHex(digit); + } + return pos; + } + + @Override public long getBytesWritten() { return buffer.getBytesWritten(); Modified: tomcat/trunk/java/org/apache/coyote/http11/filters/GzipOutputFilter.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/filters/GzipOutputFilter.java?rev=1758257&r1=1758256&r2=1758257&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http11/filters/GzipOutputFilter.java (original) +++ tomcat/trunk/java/org/apache/coyote/http11/filters/GzipOutputFilter.java Mon Aug 29 16:31:46 2016 @@ -19,6 +19,7 @@ package org.apache.coyote.http11.filters import java.io.IOException; import java.io.OutputStream; +import java.nio.ByteBuffer; import java.util.zip.GZIPOutputStream; import org.apache.coyote.OutputBuffer; @@ -73,6 +74,23 @@ public class GzipOutputFilter implements } + @Override + public int doWrite(ByteBuffer chunk) throws IOException { + if (compressionStream == null) { + compressionStream = new GZIPOutputStream(fakeOutputStream, true); + } + int len = chunk.remaining(); + if (chunk.hasArray()) { + compressionStream.write(chunk.array(), chunk.arrayOffset() + chunk.position(), len); + } else { + byte[] bytes = new byte[len]; + chunk.put(bytes); + compressionStream.write(bytes, 0, len); + } + return len; + } + + @Override public long getBytesWritten() { return buffer.getBytesWritten(); Modified: tomcat/trunk/java/org/apache/coyote/http11/filters/IdentityOutputFilter.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/filters/IdentityOutputFilter.java?rev=1758257&r1=1758256&r2=1758257&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http11/filters/IdentityOutputFilter.java (original) +++ tomcat/trunk/java/org/apache/coyote/http11/filters/IdentityOutputFilter.java Mon Aug 29 16:31:46 2016 @@ -18,6 +18,7 @@ package org.apache.coyote.http11.filters; import java.io.IOException; +import java.nio.ByteBuffer; import org.apache.coyote.OutputBuffer; import org.apache.coyote.Response; @@ -88,6 +89,44 @@ public class IdentityOutputFilter implem } return result; + + } + + + @Override + public int doWrite(ByteBuffer chunk) throws IOException { + + int result = -1; + + if (contentLength >= 0) { + if (remaining > 0) { + result = chunk.remaining(); + if (result > remaining) { + // The chunk is longer than the number of bytes remaining + // in the body; changing the chunk length to the number + // of bytes remaining + chunk.limit(chunk.position() + (int) remaining); + result = (int) remaining; + remaining = 0; + } else { + remaining = remaining - result; + } + buffer.doWrite(chunk); + } else { + // No more bytes left to be written : return -1 and clear the + // buffer + chunk.position(0); + chunk.limit(0); + result = -1; + } + } else { + // If no content length was set, just write the bytes + result = chunk.remaining(); + buffer.doWrite(chunk); + result -= chunk.remaining(); + } + + return result; } Modified: tomcat/trunk/java/org/apache/coyote/http11/filters/VoidOutputFilter.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/filters/VoidOutputFilter.java?rev=1758257&r1=1758256&r2=1758257&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http11/filters/VoidOutputFilter.java (original) +++ tomcat/trunk/java/org/apache/coyote/http11/filters/VoidOutputFilter.java Mon Aug 29 16:31:46 2016 @@ -18,6 +18,7 @@ package org.apache.coyote.http11.filters; import java.io.IOException; +import java.nio.ByteBuffer; import org.apache.coyote.OutputBuffer; import org.apache.coyote.Response; @@ -41,6 +42,12 @@ public class VoidOutputFilter implements } + @Override + public int doWrite(ByteBuffer chunk) throws IOException { + return chunk.remaining(); + } + + @Override public long getBytesWritten() { return 0; Modified: tomcat/trunk/java/org/apache/coyote/http2/Stream.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http2/Stream.java?rev=1758257&r1=1758256&r2=1758257&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http2/Stream.java (original) +++ tomcat/trunk/java/org/apache/coyote/http2/Stream.java Mon Aug 29 16:31:46 2016 @@ -489,6 +489,35 @@ public class Stream extends AbstractStre return offset; } + @Override + public synchronized int doWrite(ByteBuffer chunk) throws IOException { + if (closed) { + throw new IllegalStateException( + sm.getString("stream.closed", getConnectionId(), getIdentifier())); + } + if (!coyoteResponse.isCommitted()) { + coyoteResponse.sendHeaders(); + } + int chunkLimit = chunk.limit(); + int offset = 0; + while (chunk.remaining() > 0) { + int thisTime = Math.min(buffer.remaining(), chunk.remaining()); + chunk.limit(chunk.position() + thisTime); + buffer.put(chunk); + chunk.limit(chunkLimit); + offset += thisTime; + if (chunk.remaining() > 0 && !buffer.hasRemaining()) { + // Only flush if we have more data to write and the buffer + // is full + if (flush(true, coyoteResponse.getWriteListener() == null)) { + break; + } + } + } + written += offset; + return offset; + } + public synchronized boolean flush(boolean block) throws IOException { return flush(false, block); } Modified: tomcat/trunk/test/org/apache/coyote/http11/filters/TesterOutputBuffer.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/coyote/http11/filters/TesterOutputBuffer.java?rev=1758257&r1=1758256&r2=1758257&view=diff ============================================================================== --- tomcat/trunk/test/org/apache/coyote/http11/filters/TesterOutputBuffer.java (original) +++ tomcat/trunk/test/org/apache/coyote/http11/filters/TesterOutputBuffer.java Mon Aug 29 16:31:46 2016 @@ -18,6 +18,7 @@ package org.apache.coyote.http11.filters import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.nio.ByteBuffer; import org.apache.coyote.OutputBuffer; import org.apache.coyote.Response; @@ -110,6 +111,11 @@ public class TesterOutputBuffer extends } @Override + public int doWrite(ByteBuffer chunk) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override public long getBytesWritten() { return byteCount; } --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org