responsestream.java
来自「RESIN 3.2 最新源码」· Java 代码 · 共 811 行 · 第 1/2 页
JAVA
811 行
/* * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */package com.caucho.server.connection;import com.caucho.log.Log;import com.caucho.server.webapp.WebApp;import com.caucho.util.L10N;import com.caucho.vfs.ClientDisconnectException;import com.caucho.vfs.WriteStream;import javax.servlet.ServletContext;import java.io.IOException;import java.io.OutputStream;import java.util.*;import java.util.logging.Level;import java.util.logging.Logger;class ResponseStream extends ToByteResponseStream { static final Logger log = Logger.getLogger(ResponseStream.class.getName()); static final L10N L = new L10N(ResponseStream.class); private static final int _tailChunkedLength = 7; private static final byte []_tailChunked = new byte[] {'\r', '\n', '0', '\r', '\n', '\r', '\n'}; private final AbstractHttpResponse _response; private WriteStream _next; private OutputStream _cacheStream; private long _cacheMaxLength; // used for the direct copy and caching private int _bufferStartOffset; private boolean _chunkedEncoding; private int _bufferSize; private boolean _disableAutoFlush; // bytes actually written private int _contentLength; // True for the first chunk private boolean _isFirst; private boolean _isDisconnected; private boolean _isCommitted; private boolean _allowFlush = true; private boolean _isHead = false; private boolean _isClosed = false; private final byte []_buffer = new byte[16]; ResponseStream(AbstractHttpResponse response) { _response = response; } public void init(WriteStream next) { _next = next; } /** * initializes the Response stream at the beginning of a request. */ public void start() { super.start(); _chunkedEncoding = false; _contentLength = 0; _allowFlush = true; _disableAutoFlush = false; _isClosed = false; _isHead = false; _cacheStream = null; _isDisconnected = false; _isCommitted = false; _isFirst = true; _bufferStartOffset = 0; } /** * Returns true for a Caucho response stream. */ public boolean isCauchoResponseStream() { return true; } /** * Sets the underlying cache stream for a cached request. * * @param cache the cache stream. */ public void setByteCacheStream(OutputStream cacheStream) { _cacheStream = cacheStream; CauchoRequest req = _response.getRequest(); WebApp app = req.getWebApp(); _cacheMaxLength = app.getCacheMaxLength(); } /** * Response stream is a writable stream. */ public boolean canWrite() { return true; } void setFlush(boolean flush) { _allowFlush = flush; } public void setAutoFlush(boolean isAutoFlush) { setDisableAutoFlush(! isAutoFlush); } void setDisableAutoFlush(boolean disable) { _disableAutoFlush = disable; } public void setHead() { _isHead = true; _bufferSize = 0; } public final boolean isHead() { return _isHead; } public int getContentLength() { return _contentLength; } public void setBufferSize(int size) { if (isCommitted()) throw new IllegalStateException(L.l("Buffer size cannot be set after commit")); super.setBufferSize(size); } public boolean isCommitted() { // jsp/17ec return _isCommitted || _isClosed; } public void clear() throws IOException { clearBuffer(); if (_isCommitted) throw new IOException(L.l("can't clear response after writing headers")); } public void clearBuffer() { super.clearBuffer(); if (! _isCommitted) { // jsp/15la _isFirst = true; _bufferStartOffset = 0; _response.setHeaderWritten(false); } _next.setBufferOffset(_bufferStartOffset); } /** * Clear the closed state, because of the NOT_MODIFIED */ public void clearClosed() { _isClosed = false; } private void writeHeaders(int length) throws IOException { _chunkedEncoding = _response.writeHeaders(_next, length); } /** * Returns the byte buffer. */ public byte []getBuffer() throws IOException { flushBuffer(); return _next.getBuffer(); } /** * Returns the byte offset. */ public int getBufferOffset() throws IOException { byte []buffer; int offset; flushBuffer(); offset = _next.getBufferOffset(); if (! _chunkedEncoding) { _bufferStartOffset = offset; return offset; } else if (_bufferStartOffset > 0) { return offset; } // chunked allocates 8 bytes for the chunk header buffer = _next.getBuffer(); if (buffer.length - offset < 8) { _isCommitted = true; _next.flushBuffer(); buffer = _next.getBuffer(); offset = _next.getBufferOffset(); } _bufferStartOffset = offset + 8; _next.setBufferOffset(offset + 8); return _bufferStartOffset; } /** * Sets the next buffer */ public byte []nextBuffer(int offset) throws IOException { if (_isClosed) return _next.getBuffer(); _isCommitted = true; int startOffset = _bufferStartOffset; _bufferStartOffset = 0; int length = offset - startOffset; long lengthHeader = _response.getContentLengthHeader(); if (lengthHeader > 0 && lengthHeader < _contentLength + length) { lengthException(_next.getBuffer(), startOffset, length, lengthHeader); length = (int) (lengthHeader - _contentLength); offset = startOffset + length; } _contentLength += length; try { if (_isHead) { return _next.getBuffer(); } else if (_chunkedEncoding) { if (length == 0) throw new IllegalStateException(); byte []buffer = _next.getBuffer(); writeChunk(buffer, startOffset, length); buffer = _next.nextBuffer(offset); if (log.isLoggable(Level.FINE)) log.fine(dbgId() + "write-chunk(" + offset + ")"); _bufferStartOffset = 8 + _next.getBufferOffset(); _next.setBufferOffset(_bufferStartOffset); return buffer; } else { if (_cacheStream != null) writeCache(_next.getBuffer(), startOffset, length); byte []buffer = _next.nextBuffer(offset); if (log.isLoggable(Level.FINE)) log.fine(dbgId() + "write-chunk(" + offset + ")"); return buffer; } } catch (ClientDisconnectException e) { _response.killCache(); if (_response.isIgnoreClientDisconnect()) { _isDisconnected = true; return _next.getBuffer(); } else throw e; } catch (IOException e) { _response.killCache(); throw e; } } /** * Sets the byte offset. */ public void setBufferOffset(int offset) throws IOException { if (_isClosed) return; int startOffset = _bufferStartOffset; if (offset == startOffset) return; int length = offset - startOffset; long lengthHeader = _response.getContentLengthHeader(); if (lengthHeader > 0 && lengthHeader < _contentLength + length) { lengthException(_next.getBuffer(), startOffset, length, lengthHeader); length = (int) (lengthHeader - _contentLength); offset = startOffset + length; } _contentLength += length; if (_cacheStream != null && ! _chunkedEncoding) { _bufferStartOffset = offset; writeCache(_next.getBuffer(), startOffset, length); } if (log.isLoggable(Level.FINE)) log.fine(dbgId() + "write-chunk(" + length + ")"); if (! _isHead) { _next.setBufferOffset(offset); } } /** * Writes the next chunk of data to the response stream. * * @param buf the buffer containing the data * @param offset start offset into the buffer * @param length length of the data in the buffer */ protected void writeNext(byte []buf, int offset, int length, boolean isFinished) throws IOException { try { if (_isClosed) return; if (_disableAutoFlush && ! isFinished) throw new IOException(L.l("auto-flushing has been disabled")); boolean isFirst = _isFirst; _isFirst = false; if (! isFirst) {
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?