use of io.undertow.connector.PooledByteBuffer in project undertow by undertow-io.
the class SslConduit method doUnwrap.
/**
* Unwrap channel data into the user buffers. If no user buffer is supplied (e.g. during handshaking) then the
* unwrap will happen into the channels unwrap buffer.
*
* If some data has already been unwrapped it will simply be copied into the user buffers
* and no unwrap will actually take place.
*
* @return true if the unwrap operation made progress, false otherwise
* @throws SSLException
*/
private long doUnwrap(ByteBuffer[] userBuffers, int off, int len) throws IOException {
if (anyAreSet(state, FLAG_CLOSED)) {
throw new ClosedChannelException();
}
if (outstandingTasks > 0) {
return 0;
}
if (anyAreSet(state, FLAG_READ_REQUIRES_WRITE)) {
doWrap(null, 0, 0);
if (allAreClear(state, FLAG_WRITE_REQUIRES_READ)) {
//unless a wrap is immediately required we just return
return 0;
}
}
boolean bytesProduced = false;
PooledByteBuffer unwrappedData = this.unwrappedData;
//copy any exiting data
if (unwrappedData != null) {
if (userBuffers != null) {
long copied = Buffers.copy(userBuffers, off, len, unwrappedData.getBuffer());
if (!unwrappedData.getBuffer().hasRemaining()) {
unwrappedData.close();
this.unwrappedData = null;
}
if (copied > 0) {
readListenerInvocationCount = 0;
}
return copied;
}
}
try {
//we need to store how much data is in the unwrap buffer. If no progress can be made then we unset
//the data to unwrap flag
int dataToUnwrapLength;
//try and read some data if we don't already have some
if (allAreClear(state, FLAG_DATA_TO_UNWRAP)) {
if (dataToUnwrap == null) {
dataToUnwrap = bufferPool.allocate();
}
int res;
try {
res = source.read(dataToUnwrap.getBuffer());
} catch (IOException e) {
dataToUnwrap.close();
dataToUnwrap = null;
throw e;
}
dataToUnwrap.getBuffer().flip();
if (res == -1) {
dataToUnwrap.close();
dataToUnwrap = null;
notifyReadClosed();
return -1;
} else if (res == 0 && engine.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.FINISHED) {
return 0;
}
}
dataToUnwrapLength = dataToUnwrap.getBuffer().remaining();
long original = 0;
if (userBuffers != null) {
original = Buffers.remaining(userBuffers);
}
//perform the actual unwrap operation
//if possible this is done into the the user buffers, however
//if none are supplied or this results in a buffer overflow then we allocate our own
SSLEngineResult result;
boolean unwrapBufferUsed = false;
try {
if (userBuffers != null) {
result = engine.unwrap(this.dataToUnwrap.getBuffer(), userBuffers, off, len);
if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
//not enough space in the user buffers
//we use our own
unwrappedData = bufferPool.allocate();
ByteBuffer[] d = new ByteBuffer[len + 1];
System.arraycopy(userBuffers, off, d, 0, len);
d[len] = unwrappedData.getBuffer();
result = engine.unwrap(this.dataToUnwrap.getBuffer(), d);
unwrapBufferUsed = true;
}
bytesProduced = result.bytesProduced() > 0;
} else {
unwrapBufferUsed = true;
if (unwrappedData == null) {
unwrappedData = bufferPool.allocate();
} else {
unwrappedData.getBuffer().compact();
}
result = engine.unwrap(this.dataToUnwrap.getBuffer(), unwrappedData.getBuffer());
bytesProduced = result.bytesProduced() > 0;
}
} finally {
if (unwrapBufferUsed) {
unwrappedData.getBuffer().flip();
if (!unwrappedData.getBuffer().hasRemaining()) {
unwrappedData.close();
unwrappedData = null;
}
}
this.unwrappedData = unwrappedData;
}
if (!handleHandshakeResult(result)) {
if (this.dataToUnwrap.getBuffer().hasRemaining() && result.getStatus() != SSLEngineResult.Status.BUFFER_UNDERFLOW && dataToUnwrap.getBuffer().remaining() != dataToUnwrapLength) {
state |= FLAG_DATA_TO_UNWRAP;
} else {
state &= ~FLAG_DATA_TO_UNWRAP;
}
return 0;
}
if (result.getStatus() == SSLEngineResult.Status.CLOSED) {
if (dataToUnwrap != null) {
dataToUnwrap.close();
dataToUnwrap = null;
}
notifyReadClosed();
return -1;
}
if (result.getStatus() == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
state &= ~FLAG_DATA_TO_UNWRAP;
} else if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
UndertowLogger.REQUEST_LOGGER.sslBufferOverflow(this);
IoUtils.safeClose(delegate);
} else if (this.dataToUnwrap.getBuffer().hasRemaining() && dataToUnwrap.getBuffer().remaining() != dataToUnwrapLength) {
state |= FLAG_DATA_TO_UNWRAP;
} else {
state &= ~FLAG_DATA_TO_UNWRAP;
}
if (userBuffers == null) {
return 0;
} else {
long res = original - Buffers.remaining(userBuffers);
if (res > 0) {
//if data has been successfully returned this is not a read loop
readListenerInvocationCount = 0;
}
return res;
}
} catch (RuntimeException | IOException e) {
close();
throw e;
} finally {
//if there is data in the buffer and reads are resumed we should re-run the listener
boolean requiresListenerInvocation = false;
//we always need to re-invoke if bytes have been produced, as the engine may have buffered some data
if (bytesProduced || (unwrappedData != null && unwrappedData.isOpen() && unwrappedData.getBuffer().hasRemaining())) {
requiresListenerInvocation = true;
}
if (dataToUnwrap != null) {
//if there is no data in the buffer we just free it
if (!dataToUnwrap.getBuffer().hasRemaining()) {
dataToUnwrap.close();
dataToUnwrap = null;
state &= ~FLAG_DATA_TO_UNWRAP;
} else if (allAreClear(state, FLAG_DATA_TO_UNWRAP)) {
//if there is not enough data in the buffer we compact it to make room for more
dataToUnwrap.getBuffer().compact();
} else {
//there is more data, make sure we trigger a read listener invocation
requiresListenerInvocation = true;
}
}
//as it is about to be invoked anyway
if (requiresListenerInvocation && (anyAreSet(state, FLAG_READS_RESUMED) || allAreSet(state, FLAG_WRITE_REQUIRES_READ | FLAG_WRITES_RESUMED)) && !invokingReadListenerHandshake) {
runReadListener(false);
}
}
}
use of io.undertow.connector.PooledByteBuffer in project undertow by undertow-io.
the class Http2SettingsStreamSinkChannel method createFrameHeaderImpl.
@Override
protected SendFrameHeader createFrameHeaderImpl() {
PooledByteBuffer pooled = getChannel().getBufferPool().allocate();
ByteBuffer currentBuffer = pooled.getBuffer();
if (settings != null) {
int size = settings.size() * 6;
currentBuffer.put((byte) ((size >> 16) & 0xFF));
currentBuffer.put((byte) ((size >> 8) & 0xFF));
currentBuffer.put((byte) (size & 0xFF));
//type
currentBuffer.put((byte) Http2Channel.FRAME_TYPE_SETTINGS);
//flags
currentBuffer.put((byte) 0);
Http2ProtocolUtils.putInt(currentBuffer, getStreamId());
for (Http2Setting setting : settings) {
currentBuffer.put((byte) ((setting.getId() >> 8) & 0xFF));
currentBuffer.put((byte) (setting.getId() & 0xFF));
currentBuffer.put((byte) ((setting.getValue() >> 24) & 0xFF));
currentBuffer.put((byte) ((setting.getValue() >> 16) & 0xFF));
currentBuffer.put((byte) ((setting.getValue() >> 8) & 0xFF));
currentBuffer.put((byte) (setting.getValue() & 0xFF));
}
} else {
currentBuffer.put((byte) 0);
currentBuffer.put((byte) 0);
currentBuffer.put((byte) 0);
//type
currentBuffer.put((byte) Http2Channel.FRAME_TYPE_SETTINGS);
//flags
currentBuffer.put((byte) Http2Channel.SETTINGS_FLAG_ACK);
Http2ProtocolUtils.putInt(currentBuffer, getStreamId());
}
currentBuffer.flip();
return new SendFrameHeader(pooled);
}
use of io.undertow.connector.PooledByteBuffer in project undertow by undertow-io.
the class AjpReadListener method handleEvent.
public void handleEvent(final StreamSourceChannel channel) {
if (connection.getOriginalSinkConduit().isWriteShutdown() || connection.getOriginalSourceConduit().isReadShutdown()) {
safeClose(connection);
channel.suspendReads();
return;
}
PooledByteBuffer existing = connection.getExtraBytes();
final PooledByteBuffer pooled = existing == null ? connection.getByteBufferPool().allocate() : existing;
final ByteBuffer buffer = pooled.getBuffer();
boolean free = true;
boolean bytesRead = false;
try {
int res;
do {
if (existing == null) {
buffer.clear();
try {
res = channel.read(buffer);
} catch (IOException e) {
UndertowLogger.REQUEST_IO_LOGGER.ioException(e);
safeClose(connection);
return;
}
} else {
res = buffer.remaining();
}
if (res == 0) {
if (bytesRead && parseTimeoutUpdater != null) {
parseTimeoutUpdater.failedParse();
}
if (!channel.isReadResumed()) {
channel.getReadSetter().set(this);
channel.resumeReads();
}
return;
}
if (res == -1) {
try {
channel.shutdownReads();
final StreamSinkChannel responseChannel = connection.getChannel().getSinkChannel();
responseChannel.shutdownWrites();
safeClose(connection);
} catch (IOException e) {
UndertowLogger.REQUEST_IO_LOGGER.ioException(e);
// fuck it, it's all ruined
safeClose(connection);
return;
}
return;
}
bytesRead = true;
//TODO: we need to handle parse errors
if (existing != null) {
existing = null;
connection.setExtraBytes(null);
} else {
buffer.flip();
}
int begin = buffer.remaining();
if (httpServerExchange == null) {
httpServerExchange = new HttpServerExchange(connection, maxEntitySize);
}
parser.parse(buffer, state, httpServerExchange);
read += begin - buffer.remaining();
if (buffer.hasRemaining()) {
free = false;
connection.setExtraBytes(pooled);
}
if (read > maxRequestSize) {
UndertowLogger.REQUEST_LOGGER.requestHeaderWasTooLarge(connection.getPeerAddress(), maxRequestSize);
safeClose(connection);
return;
}
} while (!state.isComplete());
if (parseTimeoutUpdater != null) {
parseTimeoutUpdater.requestStarted();
}
if (state.prefix != AjpRequestParser.FORWARD_REQUEST) {
if (state.prefix == AjpRequestParser.CPING) {
UndertowLogger.REQUEST_LOGGER.debug("Received CPING, sending CPONG");
handleCPing();
} else if (state.prefix == AjpRequestParser.CPONG) {
UndertowLogger.REQUEST_LOGGER.debug("Received CPONG, starting next request");
state = new AjpRequestParseState();
channel.getReadSetter().set(this);
channel.resumeReads();
} else {
UndertowLogger.REQUEST_LOGGER.ignoringAjpRequestWithPrefixCode(state.prefix);
safeClose(connection);
}
return;
}
// we remove ourselves as the read listener from the channel;
// if the http handler doesn't set any then reads will suspend, which is the right thing to do
channel.getReadSetter().set(null);
channel.suspendReads();
final HttpServerExchange httpServerExchange = this.httpServerExchange;
final AjpServerResponseConduit responseConduit = new AjpServerResponseConduit(connection.getChannel().getSinkChannel().getConduit(), connection.getByteBufferPool(), httpServerExchange, new ConduitListener<AjpServerResponseConduit>() {
@Override
public void handleEvent(AjpServerResponseConduit channel) {
Connectors.terminateResponse(httpServerExchange);
}
}, httpServerExchange.getRequestMethod().equals(Methods.HEAD));
connection.getChannel().getSinkChannel().setConduit(responseConduit);
connection.getChannel().getSourceChannel().setConduit(createSourceConduit(connection.getChannel().getSourceChannel().getConduit(), responseConduit, httpServerExchange));
//we need to set the write ready handler. This allows the response conduit to wrap it
responseConduit.setWriteReadyHandler(writeReadyHandler);
try {
connection.setSSLSessionInfo(state.createSslSessionInfo());
httpServerExchange.setSourceAddress(state.createPeerAddress());
httpServerExchange.setDestinationAddress(state.createDestinationAddress());
if (scheme != null) {
httpServerExchange.setRequestScheme(scheme);
}
if (state.attributes != null) {
httpServerExchange.putAttachment(HttpServerExchange.REQUEST_ATTRIBUTES, state.attributes);
}
AjpRequestParseState oldState = state;
state = null;
this.httpServerExchange = null;
httpServerExchange.setPersistent(true);
if (recordRequestStartTime) {
Connectors.setRequestStartTime(httpServerExchange);
}
connection.setCurrentExchange(httpServerExchange);
if (connectorStatistics != null) {
connectorStatistics.setup(httpServerExchange);
}
if (oldState.badRequest) {
httpServerExchange.setStatusCode(StatusCodes.BAD_REQUEST);
httpServerExchange.endExchange();
} else {
Connectors.executeRootHandler(connection.getRootHandler(), httpServerExchange);
}
} catch (Throwable t) {
//TODO: we should attempt to return a 500 status code in this situation
UndertowLogger.REQUEST_LOGGER.exceptionProcessingRequest(t);
safeClose(connection);
}
} catch (Exception e) {
UndertowLogger.REQUEST_LOGGER.exceptionProcessingRequest(e);
safeClose(connection);
} finally {
if (free)
pooled.close();
}
}
use of io.undertow.connector.PooledByteBuffer in project undertow by undertow-io.
the class PipeliningBufferingStreamSinkConduit method write.
@Override
public long write(ByteBuffer[] srcs, int offset, int length) throws IOException {
if (anyAreSet(state, SHUTDOWN)) {
throw new ClosedChannelException();
}
if (anyAreSet(state, FLUSHING)) {
boolean res = flushBuffer();
if (!res) {
return 0;
}
}
PooledByteBuffer pooled = this.buffer;
if (pooled == null) {
this.buffer = pooled = pool.allocate();
}
final ByteBuffer buffer = pooled.getBuffer();
long total = Buffers.remaining(srcs, offset, length);
if (buffer.remaining() > total) {
long put = total;
Buffers.copy(buffer, srcs, offset, length);
return put;
} else {
return flushBufferWithUserData(srcs, offset, length);
}
}
use of io.undertow.connector.PooledByteBuffer in project undertow by undertow-io.
the class AjpServerResponseConduit method write.
public int write(final ByteBuffer src) throws IOException {
if (queuedDataLength() > 0) {
//otherwise the queue can grow indefinitely
if (!flushQueuedData()) {
return 0;
}
}
processAJPHeader();
if (headRequest) {
int remaining = src.remaining();
src.position(src.position() + remaining);
return remaining;
}
int limit = src.limit();
try {
int maxData = exchange.getConnection().getUndertowOptions().get(UndertowOptions.MAX_AJP_PACKET_SIZE, DEFAULT_MAX_DATA_SIZE) - 8;
if (src.remaining() > maxData) {
src.limit(src.position() + maxData);
}
final int writeSize = src.remaining();
final ByteBuffer[] buffers = createHeader(src);
int toWrite = 0;
for (ByteBuffer buffer : buffers) {
toWrite += buffer.remaining();
}
final int originalPayloadSize = writeSize;
long r = 0;
do {
r = super.write(buffers, 0, buffers.length);
toWrite -= r;
if (r == -1) {
throw new ClosedChannelException();
} else if (r == 0) {
//we need to copy all the remaining bytes
//TODO: this assumes the buffer is big enough
PooledByteBuffer newPooledBuffer = pool.allocate();
while (src.hasRemaining()) {
newPooledBuffer.getBuffer().put(src);
}
newPooledBuffer.getBuffer().flip();
ByteBuffer[] savedBuffers = new ByteBuffer[3];
savedBuffers[0] = buffers[0];
savedBuffers[1] = newPooledBuffer.getBuffer();
savedBuffers[2] = buffers[2];
queueFrame(new PooledBufferFrameCallback(newPooledBuffer), savedBuffers);
return originalPayloadSize;
}
} while (toWrite > 0);
return originalPayloadSize;
} catch (IOException | RuntimeException e) {
IoUtils.safeClose(exchange.getConnection());
throw e;
} finally {
src.limit(limit);
}
}
Aggregations