org.apache.catalina.connector.Response是org.apache.coyoter.Response的包装器。
protected org.apache.coyote.Response coyoteResponse;
/**
* Set the Coyote response.
*
* @param coyoteResponse The Coyote response
*/
public void setCoyoteResponse(org.apache.coyote.Response coyoteResponse) {
this.coyoteResponse = coyoteResponse;
outputBuffer.setResponse(coyoteResponse);
}
当调用Response#getOutpuStream()方法或者Response#getWriter()写出数据时,数据并不是直接发送出去,而是缓存在内部的OutputBuffer中。值得一提的是,getOutpuStream()方法和getWriter()只能调用其中一个,否则会抛出异常。
数据写出的铁三角如下:
/**
* The associated output buffer.
*/
protected final OutputBuffer outputBuffer;
/**
* The associated output stream.
*/
protected CoyoteOutputStream outputStream;
/**
* The associated writer.
*/
protected CoyoteWriter writer;
返回底层的CoyoteOutputStream字节输出流。该输出流将会把内容写出到outputBuffer中。
/**
* @return the servlet output stream associated with this Response.
*
* @exception IllegalStateException if getWriter
has
* already been called for this response
* @exception IOException if an input/output error occurs
*/
@Override
public ServletOutputStream getOutputStream()
throws IOException {
if (usingWriter) {
throw new IllegalStateException
(sm.getString("coyoteResponse.getOutputStream.ise"));
}
usingOutputStream = true;
if (outputStream == null) {
outputStream = new CoyoteOutputStream(outputBuffer);
}
return outputStream;
}
CoyoteoutputStream.java
protected OutputBuffer ob;
@Override
public void close() throws IOException {
ob.close();
}
OutputBuffer在关闭时会调用doFlush()刷写其内容
public void 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)
&& !coyoteResponse.getRequest().method().equals("HEAD")) {
// If this didn't cause a commit of the response, the final content
// length can be calculated. Only do this if this is not a HEAD
// request since in that case no body should have been written and
// setting a value of zero here will result in an explicit content
// length of zero being set on the response.
if (!coyoteResponse.isCommitted()) {
coyoteResponse.setContentLength(bb.remaining());
}
}
if (coyoteResponse.getStatus() == HttpServletResponse.SC_SWITCHING_PROTOCOLS) {
doFlush(true);
} else {
doFlush(false);
}
closed = true;
// 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.
*
* @param realFlush true
if this should also cause a real network flush
* @throws IOException An underlying IOException occurred
*/
protected void doFlush(boolean realFlush) throws IOException {
if (suspended) {
return;
}
try {
doFlush = true;
if (initial) {
coyoteResponse.sendHeaders();
initial = false;
}
if (cb.remaining() > 0) {
flushCharBuffer();
}
if (bb.remaining() > 0) {
flushByteBuffer();
}
} finally {
doFlush = false;
}
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()) {
throw new ClientAbortException(coyoteResponse.getErrorException());
}
}
}
private void flushByteBuffer() throws IOException {
realWriteBytes(bb.slice());
clear(bb);
}
private void flushCharBuffer() throws IOException {
realWriteChars(cb.slice());
clear(cb);
}
flush方法底层调用realWriteBytes(ByteBuffer buf) 方法。该方法作为适配器的适配方法调用coyoteResponse.doWrite(buf)方法。
// ------------------------------------------------- Bytes Handling Methods
/**
* 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
*/
public void realWriteBytes(ByteBuffer buf) throws IOException {
if (closed) {
return;
}
if (coyoteResponse == null) {
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.
throw new ClientAbortException(e);
}
}
}
coyote.Response将数据写入coyote.OutputBuffer
/**
* Write a chunk of bytes.
*
* @param chunk The ByteBuffer to write
*
* @throws IOException If an I/O error occurs during the write
*/
public void doWrite(ByteBuffer chunk) throws IOException {
int len = chunk.remaining();
outputBuffer.doWrite(chunk);
contentWritten += len - chunk.remaining();
}
Http11OutputBuffer
@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);
}
}
Http11OutputBuffer初始化
SocketOutputBuffer是Http11OutputBuffer的内部类。
/**
* Underlying output buffer.
*/
protected HttpOutputBuffer outputStreamOutputBuffer;
/**
* Wrapper for socket where data will be written to.
*/
protected SocketWrapperBase> socketWrapper;
protected Http11OutputBuffer(Response response, int headerBufferSize) {
................
outputStreamOutputBuffer = new SocketOutputBuffer();
}
public void init(SocketWrapperBase> socketWrapper) {
this.socketWrapper = socketWrapper;
}
SocketoutputBuffer将数据写入socketChannel
/**
* This class is an output buffer which will write data to a socket.
*/
protected class SocketOutputBuffer implements HttpOutputBuffer {
/**
* Write chunk.
*/
@Override
public int doWrite(ByteBuffer chunk) throws IOException {
try {
int len = chunk.remaining();
socketWrapper.write(isBlocking(), chunk);
len -= chunk.remaining();
byteCount += len;
return len;
} catch (IOException ioe) {
response.action(ActionCode.CLOSE_NOW, ioe);
// Re-throw
throw ioe;
}
}
@Override
public long getBytesWritten() {
return byteCount;
}
@Override
public void end() throws IOException {
socketWrapper.flush(true);
}
@Override
public void flush() throws IOException {
socketWrapper.flush(isBlocking());
}
}