/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License.
*/ package org.apache.catalina.connector;
/** * The buffer used by Tomcat response. This is a derivative of the Tomcat 3.3 OutputBuffer, with the removal of some of * the state handling (which in Coyote is mostly the Processor's responsibility). * * @author Costin Manolache * @author Remy Maucherat
*/ publicclass OutputBuffer extends Writer {
privatestaticfinal StringManager sm = StringManager.getManager(OutputBuffer.class);
/** * Is the response output suspended ? * * @return suspended flag value
*/ publicboolean isSuspended() { returnthis.suspended;
}
/** * Set the suspended flag. * * @param suspended New suspended flag value
*/ publicvoid setSuspended(boolean suspended) { this.suspended = suspended;
}
/** * Is the response output closed ? * * @return closed flag value
*/ publicboolean isClosed() { returnthis.closed;
}
// --------------------------------------------------------- Public Methods
/** * Recycle the output buffer.
*/ publicvoid recycle() {
/** * Close the output buffer. This tries to calculate the response size if the response has not been committed yet. * * @throws IOException An underlying IOException occurred
*/
@Override publicvoid close() throws IOException {
if (closed) { return;
} if (suspended) { return;
}
// If there are chars, flush all of them to the byte buffer now as bytes are used to // calculate the content-length (if everything fits into the byte buffer, of course). if (cb.remaining() > 0) {
flushCharBuffer();
}
if ((!coyoteResponse.isCommitted()) && (coyoteResponse.getContentLengthLong() == -1)) { // If this didn't cause a commit of the response, the final content // length can be calculated. if (!coyoteResponse.isCommitted()) {
coyoteResponse.setContentLength(bb.remaining());
}
}
// The request should have been completely read by the time the response // is closed. Further reads of the input a) are pointless and b) really // confuse AJP (bug 50189) so close the input buffer to prevent them.
Request req = (Request) coyoteResponse.getRequest().getNote(CoyoteAdapter.ADAPTER_NOTES);
req.inputBuffer.close();
coyoteResponse.action(ActionCode.CLOSE, null);
}
/** * Flush bytes or chars contained in the buffer. * * @throws IOException An underlying IOException occurred
*/
@Override publicvoid flush() throws IOException {
doFlush(true);
}
/** * Flush bytes or chars contained in the buffer. * * @param realFlush <code>true</code> if this should also cause a real network flush * * @throws IOException An underlying IOException occurred
*/ protectedvoid doFlush(boolean realFlush) throws IOException {
if (realFlush) {
coyoteResponse.action(ActionCode.CLIENT_FLUSH, null); // If some exception occurred earlier, or if some IOE occurred // here, notify the servlet with an IOE if (coyoteResponse.isExceptionPresent()) { thrownew ClientAbortException(coyoteResponse.getErrorException());
}
}
/** * Sends the buffer data to the client output, checking the state of Response and calling the right interceptors. * * @param buf the ByteBuffer to be written to the response * * @throws IOException An underlying IOException occurred
*/ publicvoid realWriteBytes(ByteBuffer buf) throws IOException {
if (closed) { return;
}
// If we really have something to write if (buf.remaining() > 0) { // real write to the adapter try {
coyoteResponse.doWrite(buf);
} catch (CloseNowException e) { // Catch this sub-class as it requires specific handling. // Examples where this exception is thrown: // - HTTP/2 stream timeout // Prevent further output for this response
closed = true; throw e;
} catch (IOException e) { // An IOException on a write is almost always due to // the remote client aborting the request. Wrap this // so that it can be handled better by the error dispatcher.
coyoteResponse.setErrorException(e); thrownew ClientAbortException(e);
}
}
}
publicvoid write(byte b[], int off, int len) throws IOException {
/** * Convert the chars to bytes, then send the data to the client. * * @param from Char buffer to be written to the response * * @throws IOException An underlying IOException occurred
*/ publicvoid realWriteChars(CharBuffer from) throws IOException {
while (from.remaining() > 0) {
conv.convert(from, bb); if (bb.remaining() == 0) { // Break out of the loop if more chars are needed to produce any output break;
} if (from.remaining() > 0) {
flushByteBuffer();
} elseif (conv.isUndeflow() && bb.limit() > bb.capacity() - 4) { // Handle an edge case. There are no more chars to write at the // moment but there is a leftover character in the converter // which must be part of a surrogate pair. The byte buffer does // not have enough space left to output the bytes for this pair // once it is complete )it will require 4 bytes) so flush now to // prevent the bytes for the leftover char and the rest of the // surrogate pair yet to be written from being lost. // See TestOutputBuffer#testUtf8SurrogateBody()
flushByteBuffer();
}
}
}
@Override publicvoid write(int c) throws IOException {
if (charset == null) { if (coyoteResponse.getCharacterEncoding() != null) { // setCharacterEncoding() was called with an invalid character set // Trigger an UnsupportedEncodingException
charset = B2CConverter.getCharset(coyoteResponse.getCharacterEncoding());
}
charset = org.apache.coyote.Constants.DEFAULT_BODY_CHARSET;
}
/** * Has this buffer been used at all? * * @return true if no chars or bytes have been added to the buffer since the last call to {@link #recycle()}
*/ publicboolean isNew() { return (bytesWritten == 0) && (charsWritten == 0);
}
/** * Add data to the buffer. * * @param src Bytes array * @param off Offset * @param len Length * * @throws IOException Writing overflow data to the output channel failed
*/ publicvoid append(byte src[], int off, int len) throws IOException { if (bb.remaining() == 0) {
appendByteArray(src, off, len);
} else { int n = transfer(src, off, len, bb);
len = len - n;
off = off + n; if (len > 0 && isFull(bb)) {
flushByteBuffer();
appendByteArray(src, off, len);
}
}
}
/** * Add data to the buffer. * * @param src Char array * @param off Offset * @param len Length * * @throws IOException Writing overflow data to the output channel failed
*/ publicvoid append(char src[], int off, int len) throws IOException { // if we have limit and we're below if (len <= cb.capacity() - cb.limit()) {
transfer(src, off, len, cb); return;
}
// Optimization: // If len-avail < length ( i.e. after we fill the buffer with // what we can, the remaining will fit in the buffer ) we'll just // copy the first part, flush, then copy the second part - 1 write // and still have some space for more. We'll still have 2 writes, but // we write more on the first. if (len + cb.limit() < 2 * cb.capacity()) { /* * If the request length exceeds the size of the output buffer, flush the output buffer and then write the * data directly. We can't avoid 2 writes, but we can write more on the second
*/ int n = transfer(src, off, len, cb);
flushCharBuffer();
transfer(src, off + n, len - n, cb);
} else { // long write - flush the buffer and write the rest // directly from source
flushCharBuffer();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.