| 976 | |
| 977 | |
| 978 | class StreamOutputBuffer implements HttpOutputBuffer, WriteBuffer.Sink { |
| 979 | |
| 980 | private final Lock writeLock = new ReentrantLock(); |
| 981 | private final ByteBuffer buffer = ByteBuffer.allocate(8 * 1024); |
| 982 | private final WriteBuffer writeBuffer = new WriteBuffer(32 * 1024); |
| 983 | // Flag that indicates that data was left over on a previous |
| 984 | // non-blocking write. Once set, this flag stays set until all the data |
| 985 | // has been written. |
| 986 | private boolean dataLeft; |
| 987 | private volatile long written = 0; |
| 988 | private int streamReservation = 0; |
| 989 | private volatile boolean closed = false; |
| 990 | private volatile StreamException reset = null; |
| 991 | private volatile boolean endOfStreamSent = false; |
| 992 | |
| 993 | /* |
| 994 | * The write methods share a common lock to ensure that only one thread at a time is able to access the buffer. |
| 995 | * Without this protection, a client that performed concurrent writes could corrupt the buffer. |
| 996 | */ |
| 997 | |
| 998 | @Override |
| 999 | public final int doWrite(ByteBuffer chunk) throws IOException { |
| 1000 | writeLock.lock(); |
| 1001 | try { |
| 1002 | if (closed) { |
| 1003 | throw new IOException(sm.getString("stream.closed", getConnectionId(), getIdAsString())); |
| 1004 | } |
| 1005 | // chunk is always fully written |
| 1006 | int result = chunk.remaining(); |
| 1007 | if (writeBuffer.isEmpty()) { |
| 1008 | int chunkLimit = chunk.limit(); |
| 1009 | while (chunk.remaining() > 0) { |
| 1010 | int thisTime = Math.min(buffer.remaining(), chunk.remaining()); |
| 1011 | chunk.limit(chunk.position() + thisTime); |
| 1012 | buffer.put(chunk); |
| 1013 | chunk.limit(chunkLimit); |
| 1014 | if (chunk.remaining() > 0 && !buffer.hasRemaining()) { |
| 1015 | // Only flush if we have more data to write and the buffer |
| 1016 | // is full |
| 1017 | if (flush(true, coyoteResponse.getWriteListener() == null)) { |
| 1018 | writeBuffer.add(chunk); |
| 1019 | dataLeft = true; |
| 1020 | break; |
| 1021 | } |
| 1022 | } |
| 1023 | } |
| 1024 | } else { |
| 1025 | writeBuffer.add(chunk); |
| 1026 | } |
| 1027 | written += result; |
| 1028 | return result; |
| 1029 | } finally { |
| 1030 | writeLock.unlock(); |
| 1031 | } |
| 1032 | } |
| 1033 | |
| 1034 | final boolean flush(boolean block) throws IOException { |
| 1035 | writeLock.lock(); |