package org.apache.coyote.http2;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Collections;
import java.util.Locale;
import java.util.Map;
import java.util.function.Supplier;
import org.apache.coyote.ActionCode;
import org.apache.coyote.CloseNowException;
import org.apache.coyote.InputBuffer;
import org.apache.coyote.Request;
import org.apache.coyote.Response;
import org.apache.coyote.http11.HttpOutputBuffer;
import org.apache.coyote.http11.OutputFilter;
import org.apache.coyote.http2.HpackDecoder.HeaderEmitter;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.http.MimeHeaders;
import org.apache.tomcat.util.http.parser.Host;
import org.apache.tomcat.util.net.ApplicationBufferHandler;
import org.apache.tomcat.util.net.WriteBuffer;
import org.apache.tomcat.util.res.StringManager;
class Stream extends AbstractNonZeroStream implements HeaderEmitter {
private static final Log log = LogFactory.getLog(Stream.class);
private static final StringManager sm = StringManager.getManager(Stream.class);
private static final int = 0;
private static final int = 1;
private static final int = 2;
private static final int = 3;
private static final MimeHeaders ;
private static final Integer HTTP_UPGRADE_STREAM = Integer.valueOf(1);
static {
Response response = new Response();
response.setStatus(100);
StreamProcessor.prepareHeaders(null, response, true, null, null);
ACK_HEADERS = response.getMimeHeaders();
}
private volatile long contentLengthReceived = 0;
private final Http2UpgradeHandler handler;
private final WindowAllocationManager allocationManager = new WindowAllocationManager(this);
private int = HEADER_STATE_START;
private StreamException = null;
private volatile Request coyoteRequest;
private volatile StringBuilder = null;
private volatile Response coyoteResponse = new Response();
private volatile StreamInputBuffer inputBuffer;
private volatile StreamOutputBuffer streamOutputBuffer = new StreamOutputBuffer();
private volatile Http2OutputBuffer http2OutputBuffer =
new Http2OutputBuffer(coyoteResponse, streamOutputBuffer);
Stream(Integer identifier, Http2UpgradeHandler handler) {
this(identifier, handler, null);
}
Stream(Integer identifier, Http2UpgradeHandler handler, Request coyoteRequest) {
super(handler.getConnectionId(), identifier);
this.handler = handler;
handler.addChild(this);
setWindowSize(handler.getRemoteSettings().getInitialWindowSize());
if (coyoteRequest == null) {
this.coyoteRequest = new Request();
this.inputBuffer = new StreamInputBuffer();
this.coyoteRequest.setInputBuffer(inputBuffer);
} else {
this.coyoteRequest = coyoteRequest;
this.inputBuffer = null;
state.receivedStartOfHeaders();
if (HTTP_UPGRADE_STREAM.equals(identifier)) {
try {
prepareRequest();
} catch (IllegalArgumentException iae) {
coyoteResponse.setStatus(400);
coyoteResponse.setError();
}
}
state.receivedEndOfStream();
}
this.coyoteRequest.setSendfile(handler.hasAsyncIO() && handler.getProtocol().getUseSendfile());
this.coyoteResponse.setOutputBuffer(http2OutputBuffer);
this.coyoteRequest.setResponse(coyoteResponse);
this.coyoteRequest.protocol().setString("HTTP/2.0");
if (this.coyoteRequest.getStartTimeNanos() < 0) {
this.coyoteRequest.setStartTimeNanos(System.nanoTime());
}
}
private void prepareRequest() {
MessageBytes hostValueMB = coyoteRequest.getMimeHeaders().getUniqueValue("host");
if (hostValueMB == null) {
throw new IllegalArgumentException();
}
hostValueMB.toBytes();
ByteChunk valueBC = hostValueMB.getByteChunk();
byte[] valueB = valueBC.getBytes();
int valueL = valueBC.getLength();
int valueS = valueBC.getStart();
int colonPos = Host.parse(hostValueMB);
if (colonPos != -1) {
int port = 0;
for (int i = colonPos + 1; i < valueL; i++) {
char c = (char) valueB[i + valueS];
if (c < '0' || c > '9') {
throw new IllegalArgumentException();
}
port = port * 10 + c - '0';
}
coyoteRequest.setServerPort(port);
valueL = colonPos;
}
char[] hostNameC = new char[valueL];
for (int i = 0; i < valueL; i++) {
hostNameC[i] = (char) valueB[i + valueS];
}
coyoteRequest.serverName().setChars(hostNameC, 0, valueL);
}
final void receiveReset(long errorCode) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("stream.reset.receive", getConnectionId(), getIdAsString(),
Long.toString(errorCode)));
}
state.receivedReset();
if (inputBuffer != null) {
inputBuffer.receiveReset();
}
cancelAllocationRequests();
}
final void cancelAllocationRequests() {
allocationManager.notifyAny();
}
@Override
final synchronized void incrementWindowSize(int windowSizeIncrement) throws Http2Exception {
boolean notify = getWindowSize() < 1;
super.incrementWindowSize(windowSizeIncrement);
if (notify && getWindowSize() > 0) {
allocationManager.notifyStream();
}
}
final synchronized int reserveWindowSize(int reservation, boolean block)
throws IOException {
long windowSize = getWindowSize();
while (windowSize < 1) {
if (!canWrite()) {
throw new CloseNowException(sm.getString("stream.notWritable",
getConnectionId(), getIdAsString()));
}
if (block) {
try {
long writeTimeout = handler.getProtocol().getStreamWriteTimeout();
allocationManager.waitForStream(writeTimeout);
windowSize = getWindowSize();
if (windowSize == 0) {
doStreamCancel(sm.getString("stream.writeTimeout"), Http2Error.ENHANCE_YOUR_CALM);
}
} catch (InterruptedException e) {
throw new IOException(e);
}
} else {
allocationManager.waitForStreamNonBlocking();
return 0;
}
}
int allocation;
if (windowSize < reservation) {
allocation = (int) windowSize;
} else {
allocation = reservation;
}
decrementWindowSize(allocation);
return allocation;
}
void doStreamCancel(String msg, Http2Error error) throws CloseNowException {
StreamException se = new StreamException(msg, error, getIdAsInt());
streamOutputBuffer.closed = true;
coyoteResponse.setError();
coyoteResponse.setErrorReported();
streamOutputBuffer.reset = se;
throw new CloseNowException(msg, se);
}
void waitForConnectionAllocation(long timeout) throws InterruptedException {
allocationManager.waitForConnection(timeout);
}
void waitForConnectionAllocationNonBlocking() {
allocationManager.waitForConnectionNonBlocking();
}
void notifyConnection() {
allocationManager.notifyConnection();
}
@Override
public final void (String name, String value) throws HpackException {
if (log.isDebugEnabled()) {
log.debug(sm.getString("stream.header.debug", getConnectionId(), getIdAsString(),
name, value));
}
if (!name.toLowerCase(Locale.US).equals(name)) {
throw new HpackException(sm.getString("stream.header.case",
getConnectionId(), getIdAsString(), name));
}
if ("connection".equals(name)) {
throw new HpackException(sm.getString("stream.header.connection",
getConnectionId(), getIdAsString()));
}
if ("te".equals(name)) {
if (!"trailers".equals(value)) {
throw new HpackException(sm.getString("stream.header.te",
getConnectionId(), getIdAsString(), value));
}
}
if (headerException != null) {
return;
}
if (name.length() == 0) {
throw new HpackException(sm.getString("stream.header.empty",
getConnectionId(), getIdAsString()));
}
boolean pseudoHeader = name.charAt(0) == ':';
if (pseudoHeader && headerState != HEADER_STATE_PSEUDO) {
headerException = new StreamException(sm.getString(
"stream.header.unexpectedPseudoHeader", getConnectionId(), getIdAsString(),
name), Http2Error.PROTOCOL_ERROR, getIdAsInt());
return;
}
if (headerState == HEADER_STATE_PSEUDO && !pseudoHeader) {
headerState = HEADER_STATE_REGULAR;
}
switch (name) {
case ":method": {
if (coyoteRequest.method().isNull()) {
coyoteRequest.method().setString(value);
} else {
throw new HpackException(sm.getString("stream.header.duplicate",
getConnectionId(), getIdAsString(), ":method" ));
}
break;
}
case ":scheme": {
if (coyoteRequest.scheme().isNull()) {
coyoteRequest.scheme().setString(value);
} else {
throw new HpackException(sm.getString("stream.header.duplicate",
getConnectionId(), getIdAsString(), ":scheme" ));
}
break;
}
case ":path": {
if (!coyoteRequest.requestURI().isNull()) {
throw new HpackException(sm.getString("stream.header.duplicate",
getConnectionId(), getIdAsString(), ":path" ));
}
if (value.length() == 0) {
throw new HpackException(sm.getString("stream.header.noPath",
getConnectionId(), getIdAsString()));
}
int queryStart = value.indexOf('?');
String uri;
if (queryStart == -1) {
uri = value;
} else {
uri = value.substring(0, queryStart);
String query = value.substring(queryStart + 1);
coyoteRequest.queryString().setString(query);
}
byte[] uriBytes = uri.getBytes(StandardCharsets.ISO_8859_1);
coyoteRequest.requestURI().setBytes(uriBytes, 0, uriBytes.length);
break;
}
case ":authority": {
if (coyoteRequest.serverName().isNull()) {
int i;
try {
i = Host.parse(value);
} catch (IllegalArgumentException iae) {
throw new HpackException(sm.getString("stream.header.invalid",
getConnectionId(), getIdAsString(), ":authority", value));
}
if (i > -1) {
coyoteRequest.serverName().setString(value.substring(0, i));
coyoteRequest.setServerPort(Integer.parseInt(value.substring(i + 1)));
} else {
coyoteRequest.serverName().setString(value);
}
} else {
throw new HpackException(sm.getString("stream.header.duplicate",
getConnectionId(), getIdAsString(), ":authority" ));
}
break;
}
case "cookie": {
if (cookieHeader == null) {
cookieHeader = new StringBuilder();
} else {
cookieHeader.append("; ");
}
cookieHeader.append(value);
break;
}
default: {
if (headerState == HEADER_STATE_TRAILER &&
!handler.getProtocol().isTrailerHeaderAllowed(name)) {
break;
}
if ("expect".equals(name) && "100-continue".equals(value)) {
coyoteRequest.setExpectation(true);
}
if (pseudoHeader) {
headerException = new StreamException(sm.getString(
"stream.header.unknownPseudoHeader", getConnectionId(), getIdAsString(),
name), Http2Error.PROTOCOL_ERROR, getIdAsInt());
}
if (headerState == HEADER_STATE_TRAILER) {
coyoteRequest.getTrailerFields().put(name, value);
} else {
coyoteRequest.getMimeHeaders().addValue(name).setString(value);
}
}
}
}
@Override
public void (StreamException streamException) {
if (headerException == null) {
headerException = streamException;
}
}
@Override
public void () throws StreamException {
if (headerException == null) {
return;
}
throw headerException;
}
final boolean () throws ConnectionException {
if (coyoteRequest.method().isNull() || coyoteRequest.scheme().isNull() ||
coyoteRequest.requestURI().isNull()) {
throw new ConnectionException(sm.getString("stream.header.required",
getConnectionId(), getIdAsString()), Http2Error.PROTOCOL_ERROR);
}
if (cookieHeader != null) {
coyoteRequest.getMimeHeaders().addValue("cookie").setString(cookieHeader.toString());
}
return headerState == HEADER_STATE_REGULAR || headerState == HEADER_STATE_PSEUDO;
}
final void () throws IOException {
boolean endOfStream = streamOutputBuffer.hasNoBody() &&
coyoteResponse.getTrailerFields() == null;
handler.writeHeaders(this, 0, coyoteResponse.getMimeHeaders(), endOfStream, Constants.DEFAULT_HEADERS_FRAME_SIZE);
}
final void addOutputFilter(OutputFilter filter) {
http2OutputBuffer.addFilter(filter);
}
final void writeTrailers() throws IOException {
Supplier<Map<String,String>> supplier = coyoteResponse.getTrailerFields();
if (supplier == null) {
return;
}
MimeHeaders mimeHeaders = coyoteResponse.getMimeHeaders();
mimeHeaders.recycle();
Map<String,String> headerMap = supplier.get();
if (headerMap == null) {
headerMap = Collections.emptyMap();
}
for (Map.Entry<String, String> headerEntry : headerMap.entrySet()) {
MessageBytes mb = mimeHeaders.addValue(headerEntry.getKey());
mb.setString(headerEntry.getValue());
}
handler.writeHeaders(this, 0, mimeHeaders, true, Constants.DEFAULT_HEADERS_FRAME_SIZE);
}
final void writeAck() throws IOException {
handler.writeHeaders(this, 0, ACK_HEADERS, false, Constants.DEFAULT_HEADERS_ACK_FRAME_SIZE);
}
@Override
final String getConnectionId() {
return handler.getConnectionId();
}
final Request getCoyoteRequest() {
return coyoteRequest;
}
final Response getCoyoteResponse() {
return coyoteResponse;
}
final ByteBuffer getInputByteBuffer() {
StreamInputBuffer inputBuffer = this.inputBuffer;
if (inputBuffer == null) {
return null;
}
return inputBuffer.getInBuffer();
}
final void (boolean headersEndStream) throws Http2Exception {
if (headerState == HEADER_STATE_START) {
headerState = HEADER_STATE_PSEUDO;
handler.getHpackDecoder().setMaxHeaderCount(handler.getProtocol().getMaxHeaderCount());
handler.getHpackDecoder().setMaxHeaderSize(handler.getProtocol().getMaxHeaderSize());
} else if (headerState == HEADER_STATE_PSEUDO || headerState == HEADER_STATE_REGULAR) {
if (headersEndStream) {
headerState = HEADER_STATE_TRAILER;
handler.getHpackDecoder().setMaxHeaderCount(
handler.getProtocol().getMaxTrailerCount());
handler.getHpackDecoder().setMaxHeaderSize(
handler.getProtocol().getMaxTrailerSize());
} else {
throw new ConnectionException(sm.getString("stream.trailerHeader.noEndOfStream",
getConnectionId(), getIdAsString()), Http2Error.PROTOCOL_ERROR);
}
}
state.receivedStartOfHeaders();
}
final void receivedData(int payloadSize) throws ConnectionException {
contentLengthReceived += payloadSize;
Request coyoteRequest = this.coyoteRequest;
if (coyoteRequest == null) {
return;
}
long contentLengthHeader = coyoteRequest.getContentLengthLong();
if (contentLengthHeader > -1 && contentLengthReceived > contentLengthHeader) {
throw new ConnectionException(sm.getString("stream.header.contentLength",
getConnectionId(), getIdAsString(), Long.valueOf(contentLengthHeader),
Long.valueOf(contentLengthReceived)), Http2Error.PROTOCOL_ERROR);
}
}
final void receivedEndOfStream() throws ConnectionException {
if (isContentLengthInconsistent()) {
throw new ConnectionException(sm.getString("stream.header.contentLength",
getConnectionId(), getIdAsString(),
Long.valueOf(coyoteRequest.getContentLengthLong()),
Long.valueOf(contentLengthReceived)), Http2Error.PROTOCOL_ERROR);
}
state.receivedEndOfStream();
if (inputBuffer != null) {
inputBuffer.notifyEof();
}
}
final boolean isContentLengthInconsistent() {
long contentLengthHeader = coyoteRequest.getContentLengthLong();
if (contentLengthHeader > -1 && contentLengthReceived != contentLengthHeader) {
return true;
}
return false;
}
final void () {
state.sentHeaders();
}
final void sentEndOfStream() {
streamOutputBuffer.endOfStreamSent = true;
state.sentEndOfStream();
}
final boolean isReadyForWrite() {
return streamOutputBuffer.isReady();
}
final boolean flush(boolean block) throws IOException {
return streamOutputBuffer.flush(block);
}
final StreamInputBuffer getInputBuffer() {
return inputBuffer;
}
final HttpOutputBuffer getOutputBuffer() {
return http2OutputBuffer;
}
final void sentPushPromise() {
state.sentPushPromise();
}
final boolean isActive() {
return state.isActive();
}
final boolean canWrite() {
return state.canWrite();
}
final void closeIfIdle() {
state.closeIfIdle();
}
final boolean isInputFinished() {
return !state.isFrameTypePermitted(FrameType.DATA);
}
final void close(Http2Exception http2Exception) {
if (http2Exception instanceof StreamException) {
try {
StreamException se = (StreamException) http2Exception;
if (log.isDebugEnabled()) {
log.debug(sm.getString("stream.reset.send", getConnectionId(), getIdAsString(),
se.getError()));
}
state.sendReset();
cancelAllocationRequests();
handler.sendStreamReset(se);
} catch (IOException ioe) {
ConnectionException ce = new ConnectionException(
sm.getString("stream.reset.fail"), Http2Error.PROTOCOL_ERROR);
ce.initCause(ioe);
handler.closeConnection(ce);
}
} else {
handler.closeConnection(http2Exception);
}
recycle();
}
final void recycle() {
if (log.isDebugEnabled()) {
log.debug(sm.getString("stream.recycle", getConnectionId(), getIdAsString()));
}
handler.replaceStream(this, new RecycledStream(getConnectionId(), getIdentifier(), state));
}
final boolean isPushSupported() {
return handler.getRemoteSettings().getEnablePush();
}
final void push(Request request) throws IOException {
if (!isPushSupported() || getIdAsInt() % 2 == 0) {
return;
}
request.getMimeHeaders().addValue(":method").duplicate(request.method());
request.getMimeHeaders().addValue(":scheme").duplicate(request.scheme());
StringBuilder path = new StringBuilder(request.requestURI().toString());
if (!request.queryString().isNull()) {
path.append('?');
path.append(request.queryString().toString());
}
request.getMimeHeaders().addValue(":path").setString(path.toString());
if (!(request.scheme().equals("http") && request.getServerPort() == 80) &&
!(request.scheme().equals("https") && request.getServerPort() == 443)) {
request.getMimeHeaders().addValue(":authority").setString(
request.serverName().getString() + ":" + request.getServerPort());
} else {
request.getMimeHeaders().addValue(":authority").duplicate(request.serverName());
}
push(handler, request, this);
}
boolean isTrailerFieldsReady() {
return !state.canRead();
}
boolean isTrailerFieldsSupported() {
return !streamOutputBuffer.endOfStreamSent;
}
StreamException getResetException() {
return streamOutputBuffer.reset;
}
private static void push(final Http2UpgradeHandler handler, final Request request,
final Stream stream) throws IOException {
if (org.apache.coyote.Constants.IS_SECURITY_ENABLED) {
try {
AccessController.doPrivileged(new PrivilegedPush(handler, request, stream));
} catch (PrivilegedActionException ex) {
Exception e = ex.getException();
if (e instanceof IOException) {
throw (IOException) e;
} else {
throw new IOException(ex);
}
}
} else {
handler.push(request, stream);
}
}
private static class PrivilegedPush implements PrivilegedExceptionAction<Void> {
private final Http2UpgradeHandler handler;
private final Request request;
private final Stream stream;
public PrivilegedPush(Http2UpgradeHandler handler, Request request,
Stream stream) {
this.handler = handler;
this.request = request;
this.stream = stream;
}
@Override
public Void run() throws IOException {
handler.push(request, stream);
return null;
}
}
class StreamOutputBuffer implements HttpOutputBuffer, WriteBuffer.Sink {
private final ByteBuffer buffer = ByteBuffer.allocate(8 * 1024);
private final WriteBuffer writeBuffer = new WriteBuffer(32 * 1024);
private boolean dataLeft;
private volatile long written = 0;
private int streamReservation = 0;
private volatile boolean closed = false;
private volatile StreamException reset = null;
private volatile boolean endOfStreamSent = false;
@Override
public final synchronized int doWrite(ByteBuffer chunk) throws IOException {
if (closed) {
throw new IllegalStateException(
sm.getString("stream.closed", getConnectionId(), getIdAsString()));
}
int result = chunk.remaining();
if (writeBuffer.isEmpty()) {
int chunkLimit = chunk.limit();
while (chunk.remaining() > 0) {
int thisTime = Math.min(buffer.remaining(), chunk.remaining());
chunk.limit(chunk.position() + thisTime);
buffer.put(chunk);
chunk.limit(chunkLimit);
if (chunk.remaining() > 0 && !buffer.hasRemaining()) {
if (flush(true, coyoteResponse.getWriteListener() == null)) {
writeBuffer.add(chunk);
dataLeft = true;
break;
}
}
}
} else {
writeBuffer.add(chunk);
}
written += result;
return result;
}
final synchronized boolean flush(boolean block) throws IOException {
boolean dataInBuffer = buffer.position() > 0;
boolean flushed = false;
if (dataInBuffer) {
dataInBuffer = flush(false, block);
flushed = true;
}
if (dataInBuffer) {
dataLeft = true;
} else {
if (writeBuffer.isEmpty()) {
if (flushed) {
dataLeft = false;
} else {
dataLeft = flush(false, block);
}
} else {
dataLeft = writeBuffer.write(this, block);
}
}
return dataLeft;
}
private final synchronized boolean flush(boolean writeInProgress, boolean block)
throws IOException {
if (log.isDebugEnabled()) {
log.debug(sm.getString("stream.outputBuffer.flush.debug", getConnectionId(),
getIdAsString(), Integer.toString(buffer.position()),
Boolean.toString(writeInProgress), Boolean.toString(closed)));
}
if (buffer.position() == 0) {
if (closed && !endOfStreamSent) {
handler.writeBody(Stream.this, buffer, 0,
coyoteResponse.getTrailerFields() == null);
}
return false;
}
buffer.flip();
int left = buffer.remaining();
while (left > 0) {
if (streamReservation == 0) {
streamReservation = reserveWindowSize(left, block);
if (streamReservation == 0) {
buffer.compact();
return true;
}
}
while (streamReservation > 0) {
int connectionReservation =
handler.reserveWindowSize(Stream.this, streamReservation, block);
if (connectionReservation == 0) {
buffer.compact();
return true;
}
handler.writeBody(Stream.this, buffer, connectionReservation,
!writeInProgress && closed && left == connectionReservation &&
coyoteResponse.getTrailerFields() == null);
streamReservation -= connectionReservation;
left -= connectionReservation;
}
}
buffer.clear();
return false;
}
final synchronized boolean isReady() {
if (getWindowSize() > 0 && allocationManager.isWaitingForStream() ||
handler.getWindowSize() > 0 && allocationManager.isWaitingForConnection() ||
dataLeft) {
return false;
} else {
return true;
}
}
@Override
public final long getBytesWritten() {
return written;
}
@Override
public final void end() throws IOException {
if (reset != null) {
throw new CloseNowException(reset);
}
if (!closed) {
closed = true;
flush(true);
writeTrailers();
}
}
final boolean hasNoBody() {
return ((written == 0) && closed);
}
@Override
public void flush() throws IOException {
flush(getCoyoteResponse().getWriteListener() == null);
}
@Override
public synchronized boolean writeFromBuffer(ByteBuffer src, boolean blocking) throws IOException {
int chunkLimit = src.limit();
while (src.remaining() > 0) {
int thisTime = Math.min(buffer.remaining(), src.remaining());
src.limit(src.position() + thisTime);
buffer.put(src);
src.limit(chunkLimit);
if (flush(false, blocking)) {
return true;
}
}
return false;
}
}
class StreamInputBuffer implements InputBuffer {
private byte[] outBuffer;
private volatile ByteBuffer inBuffer;
private volatile boolean readInterest;
private boolean resetReceived = false;
@Override
public final int doRead(ApplicationBufferHandler applicationBufferHandler)
throws IOException {
ensureBuffersExist();
int written = -1;
synchronized (inBuffer) {
boolean canRead = false;
while (inBuffer.position() == 0 && (canRead = isActive() && !isInputFinished())) {
try {
if (log.isDebugEnabled()) {
log.debug(sm.getString("stream.inputBuffer.empty"));
}
long readTimeout = handler.getProtocol().getStreamReadTimeout();
if (readTimeout < 0) {
inBuffer.wait();
} else {
inBuffer.wait(readTimeout);
}
if (resetReceived) {
throw new IOException(sm.getString("stream.inputBuffer.reset"));
}
if (inBuffer.position() == 0 && isActive() && !isInputFinished()) {
String msg = sm.getString("stream.inputBuffer.readTimeout");
StreamException se = new StreamException(
msg, Http2Error.ENHANCE_YOUR_CALM, getIdAsInt());
coyoteResponse.setError();
streamOutputBuffer.reset = se;
throw new CloseNowException(msg, se);
}
} catch (InterruptedException e) {
throw new IOException(e);
}
}
if (inBuffer.position() > 0) {
inBuffer.flip();
written = inBuffer.remaining();
if (log.isDebugEnabled()) {
log.debug(sm.getString("stream.inputBuffer.copy",
Integer.toString(written)));
}
inBuffer.get(outBuffer, 0, written);
inBuffer.clear();
} else if (!canRead) {
return -1;
} else {
throw new IllegalStateException();
}
}
applicationBufferHandler.setByteBuffer(ByteBuffer.wrap(outBuffer, 0, written));
handler.writeWindowUpdate(Stream.this, written, true);
return written;
}
final boolean isReadyForRead() {
ensureBuffersExist();
synchronized (this) {
if (available() > 0) {
return true;
}
if (!isRequestBodyFullyRead()) {
readInterest = true;
}
return false;
}
}
final synchronized boolean isRequestBodyFullyRead() {
return (inBuffer == null || inBuffer.position() == 0) && isInputFinished();
}
@Override
public final synchronized int available() {
if (inBuffer == null) {
return 0;
}
return inBuffer.position();
}
final synchronized boolean onDataAvailable() {
if (readInterest) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("stream.inputBuffer.dispatch"));
}
readInterest = false;
coyoteRequest.action(ActionCode.DISPATCH_READ, null);
coyoteRequest.action(ActionCode.DISPATCH_EXECUTE, null);
return true;
} else {
if (log.isDebugEnabled()) {
log.debug(sm.getString("stream.inputBuffer.signal"));
}
synchronized (inBuffer) {
inBuffer.notifyAll();
}
return false;
}
}
private final ByteBuffer getInBuffer() {
ensureBuffersExist();
return inBuffer;
}
final synchronized void insertReplayedBody(ByteChunk body) {
inBuffer = ByteBuffer.wrap(body.getBytes(), body.getOffset(), body.getLength());
}
private final void ensureBuffersExist() {
if (inBuffer == null) {
int size = handler.getLocalSettings().getInitialWindowSize();
synchronized (this) {
if (inBuffer == null) {
inBuffer = ByteBuffer.allocate(size);
outBuffer = new byte[size];
}
}
}
}
private final void receiveReset() {
if (inBuffer != null) {
synchronized (inBuffer) {
resetReceived = true;
inBuffer.notifyAll();
}
}
}
private final void notifyEof() {
if (inBuffer != null) {
synchronized (inBuffer) {
inBuffer.notifyAll();
}
}
}
}
}