use of io.undertow.connector.PooledByteBuffer in project undertow by undertow-io.
the class HttpReadListener method handleEventWithNoRunningRequest.
public void handleEventWithNoRunningRequest(final ConduitStreamSourceChannel channel) {
PooledByteBuffer existing = connection.getExtraBytes();
if ((existing == null && connection.getOriginalSourceConduit().isReadShutdown()) || connection.getOriginalSinkConduit().isWriteShutdown()) {
IoUtils.safeClose(connection);
channel.suspendReads();
return;
}
final PooledByteBuffer pooled = existing == null ? connection.getByteBufferPool().allocate() : existing;
final ByteBuffer buffer = pooled.getBuffer();
boolean free = true;
try {
int res;
boolean bytesRead = false;
do {
if (existing == null) {
buffer.clear();
try {
res = channel.read(buffer);
} catch (IOException e) {
UndertowLogger.REQUEST_IO_LOGGER.debug("Error reading request", e);
IoUtils.safeClose(connection);
return;
}
} else {
res = buffer.remaining();
}
if (res <= 0) {
if (bytesRead && parseTimeoutUpdater != null) {
parseTimeoutUpdater.failedParse();
}
handleFailedRead(channel, res);
return;
} else {
bytesRead = true;
}
if (existing != null) {
existing = null;
connection.setExtraBytes(null);
} else {
buffer.flip();
}
int begin = buffer.remaining();
if (httpServerExchange == null) {
httpServerExchange = new HttpServerExchange(connection, maxEntitySize);
}
parser.handle(buffer, state, httpServerExchange);
if (buffer.hasRemaining()) {
free = false;
connection.setExtraBytes(pooled);
}
int total = read + (begin - buffer.remaining());
read = total;
if (read > maxRequestSize) {
UndertowLogger.REQUEST_LOGGER.requestHeaderWasTooLarge(connection.getPeerAddress(), maxRequestSize);
sendBadRequestAndClose(connection.getChannel(), null);
return;
}
} while (!state.isComplete());
if (parseTimeoutUpdater != null) {
parseTimeoutUpdater.requestStarted();
}
final HttpServerExchange httpServerExchange = this.httpServerExchange;
httpServerExchange.setRequestScheme(connection.getSslSession() != null ? "https" : "http");
this.httpServerExchange = null;
requestStateUpdater.set(this, 1);
if (httpServerExchange.getProtocol() == Protocols.HTTP_2_0) {
free = handleHttp2PriorKnowledge(pooled, httpServerExchange);
return;
}
if (!allowUnknownProtocols) {
HttpString protocol = httpServerExchange.getProtocol();
if (protocol != Protocols.HTTP_1_1 && protocol != Protocols.HTTP_1_0 && protocol != Protocols.HTTP_0_9) {
UndertowLogger.REQUEST_IO_LOGGER.debugf("Closing connection from %s due to unknown protocol %s", connection.getChannel().getPeerAddress(), protocol);
sendBadRequestAndClose(connection.getChannel(), new IOException());
return;
}
}
HttpTransferEncoding.setupRequest(httpServerExchange);
if (recordRequestStartTime) {
Connectors.setRequestStartTime(httpServerExchange);
}
connection.setCurrentExchange(httpServerExchange);
if (connectorStatistics != null) {
connectorStatistics.setup(httpServerExchange);
}
if (connection.getSslSession() != null) {
//TODO: figure out a better solution for this
//in order to improve performance we do not generally suspend reads, instead we a CAS to detect when
//data arrives while a request is running and suspend lazily, as suspend/resume is relatively expensive
//however this approach does not work for SSL, as the underlying channel is not thread safe
//so we just suspend every time (the overhead is likely much less than the general SSL overhead anyway)
channel.suspendReads();
}
if (requireHostHeader && !httpServerExchange.getRequestHeaders().contains(Headers.HOST)) {
if (httpServerExchange.getProtocol().equals(Protocols.HTTP_1_1)) {
sendBadRequestAndClose(connection.getChannel(), UndertowMessages.MESSAGES.noHostInHttp11Request());
return;
}
}
Connectors.executeRootHandler(connection.getRootHandler(), httpServerExchange);
} catch (Exception e) {
sendBadRequestAndClose(connection.getChannel(), e);
return;
} finally {
if (free)
pooled.close();
}
}
use of io.undertow.connector.PooledByteBuffer in project undertow by undertow-io.
the class PipeliningBufferingStreamSinkConduit method write.
@Override
public int write(ByteBuffer src) 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();
if (buffer.remaining() > src.remaining()) {
int put = src.remaining();
buffer.put(src);
return put;
} else {
return (int) flushBufferWithUserData(new ByteBuffer[] { src }, 0, 1);
}
}
use of io.undertow.connector.PooledByteBuffer in project undertow by undertow-io.
the class AbstractFramedChannel method receive.
/**
* receive method, returns null if no frame is ready. Otherwise returns a
* channel that can be used to read the frame contents.
* <p>
* Calling this method can also have the side effect of making additional data available to
* existing source channels. In general if you suspend receives or don't have some other way
* of calling this method then it can prevent frame channels for being fully consumed.
*/
public synchronized R receive() throws IOException {
if (readChannelDone && receiver == null) {
//however it is much simpler just to have it here
if (readData != null) {
readData.close();
readData = null;
}
channel.getSourceChannel().suspendReads();
channel.getSourceChannel().shutdownReads();
return null;
}
ReferenceCountedPooled pooled = this.readData;
boolean hasData;
if (pooled == null) {
pooled = allocateReferenceCountedBuffer();
if (pooled == null) {
return null;
}
hasData = false;
} else if (pooled.isFreed()) {
//we attempt to re-used an existing buffer
if (!pooled.tryUnfree()) {
pooled = allocateReferenceCountedBuffer();
if (pooled == null) {
return null;
}
} else {
pooled.getBuffer().limit(pooled.getBuffer().capacity());
}
hasData = false;
} else {
hasData = pooled.getBuffer().hasRemaining();
}
boolean forceFree = false;
int read = 0;
try {
if (!hasData) {
pooled.getBuffer().clear();
read = channel.getSourceChannel().read(pooled.getBuffer());
if (read == 0) {
//no data, we just free the buffer
forceFree = true;
return null;
} else if (read == -1) {
forceFree = true;
readChannelDone = true;
lastDataRead();
return null;
} else if (isLastFrameReceived() && frameDataRemaining == 0) {
//we got data, although we should have received the last frame
forceFree = true;
markReadsBroken(new ClosedChannelException());
}
pooled.getBuffer().flip();
}
if (frameDataRemaining > 0) {
if (frameDataRemaining >= pooled.getBuffer().remaining()) {
frameDataRemaining -= pooled.getBuffer().remaining();
if (receiver != null) {
//we still create a pooled view, this means that if the buffer is still active we can re-used it
//which prevents attacks based on sending lots of small fragments
ByteBuffer buf = pooled.getBuffer().duplicate();
pooled.getBuffer().position(pooled.getBuffer().limit());
PooledByteBuffer frameData = pooled.createView(buf);
receiver.dataReady(null, frameData);
} else {
//we are dropping a frame
pooled.close();
readData = null;
}
if (frameDataRemaining == 0) {
receiver = null;
}
return null;
} else {
ByteBuffer buf = pooled.getBuffer().duplicate();
buf.limit((int) (buf.position() + frameDataRemaining));
pooled.getBuffer().position((int) (pooled.getBuffer().position() + frameDataRemaining));
frameDataRemaining = 0;
PooledByteBuffer frameData = pooled.createView(buf);
if (receiver != null) {
receiver.dataReady(null, frameData);
} else {
//we are dropping the frame
frameData.close();
}
receiver = null;
}
//and not by the selector mechanism
return null;
}
FrameHeaderData data = parseFrame(pooled.getBuffer());
if (data != null) {
PooledByteBuffer frameData;
if (data.getFrameLength() >= pooled.getBuffer().remaining()) {
frameDataRemaining = data.getFrameLength() - pooled.getBuffer().remaining();
frameData = pooled.createView(pooled.getBuffer().duplicate());
pooled.getBuffer().position(pooled.getBuffer().limit());
} else {
ByteBuffer buf = pooled.getBuffer().duplicate();
buf.limit((int) (buf.position() + data.getFrameLength()));
pooled.getBuffer().position((int) (pooled.getBuffer().position() + data.getFrameLength()));
frameData = pooled.createView(buf);
}
AbstractFramedStreamSourceChannel<?, ?, ?> existing = data.getExistingChannel();
if (existing != null) {
if (data.getFrameLength() > frameData.getBuffer().remaining()) {
receiver = (R) existing;
}
existing.dataReady(data, frameData);
if (isLastFrameReceived()) {
handleLastFrame(existing);
}
return null;
} else {
boolean moreData = data.getFrameLength() > frameData.getBuffer().remaining();
R newChannel = createChannel(data, frameData);
if (newChannel != null) {
if (!newChannel.isComplete()) {
receivers.add(newChannel);
}
if (moreData) {
receiver = newChannel;
}
if (isLastFrameReceived()) {
handleLastFrame(newChannel);
}
} else {
frameData.close();
}
return newChannel;
}
}
return null;
} catch (IOException | RuntimeException e) {
//something has code wrong with parsing, close the read side
//we don't close the write side, as the underlying implementation will most likely want to send an error
markReadsBroken(e);
forceFree = true;
throw e;
} finally {
//which will make readData null
if (readData != null) {
if (!pooled.getBuffer().hasRemaining() || forceFree) {
if (pooled.getBuffer().limit() * 2 > pooled.getBuffer().capacity() || forceFree) {
//if we have used more than half the buffer we don't allow it to be re-aquired
readData = null;
}
//even though this is freed we may un-free it if we get a new packet
//this prevents many small reads resulting in a large number of allocated buffers
pooled.close();
}
}
}
}
use of io.undertow.connector.PooledByteBuffer in project undertow by undertow-io.
the class AbstractFramedChannel method flushSenders.
/**
* Flushes all ready stream sink conduits to the channel.
* <p>
* Frames will be batched up, to allow them all to be written out via a gathering
* write. The {@link #framePriority} implementation will be invoked to decide which
* frames are eligible for sending and in what order.
*/
protected synchronized void flushSenders() {
if (flushingSenders) {
throw UndertowMessages.MESSAGES.recursiveCallToFlushingSenders();
}
flushingSenders = true;
try {
int toSend = 0;
while (!newFrames.isEmpty()) {
S frame = newFrames.poll();
frame.preWrite();
if (framePriority.insertFrame(frame, pendingFrames)) {
if (!heldFrames.isEmpty()) {
framePriority.frameAdded(frame, pendingFrames, heldFrames);
}
} else {
heldFrames.add(frame);
}
}
boolean finalFrame = false;
ListIterator<S> it = pendingFrames.listIterator();
while (it.hasNext()) {
S sender = it.next();
if (sender.isReadyForFlush()) {
++toSend;
} else {
break;
}
if (sender.isLastFrame()) {
finalFrame = true;
}
}
if (toSend == 0) {
//if there is nothing to send we just attempt a flush on the underlying channel
try {
if (channel.getSinkChannel().flush()) {
channel.getSinkChannel().suspendWrites();
}
} catch (IOException e) {
safeClose(channel);
markWritesBroken(e);
}
return;
}
ByteBuffer[] data = new ByteBuffer[toSend * 3];
int j = 0;
it = pendingFrames.listIterator();
try {
while (j < toSend) {
S next = it.next();
//todo: rather than adding empty buffers just store the offsets
SendFrameHeader frameHeader = next.getFrameHeader();
PooledByteBuffer frameHeaderByteBuffer = frameHeader.getByteBuffer();
ByteBuffer frameTrailerBuffer = frameHeader.getTrailer();
data[j * 3] = frameHeaderByteBuffer != null ? frameHeaderByteBuffer.getBuffer() : Buffers.EMPTY_BYTE_BUFFER;
data[(j * 3) + 1] = next.getBuffer() == null ? Buffers.EMPTY_BYTE_BUFFER : next.getBuffer();
data[(j * 3) + 2] = frameTrailerBuffer != null ? frameTrailerBuffer : Buffers.EMPTY_BYTE_BUFFER;
++j;
}
long toWrite = Buffers.remaining(data);
long res;
do {
res = channel.getSinkChannel().write(data);
toWrite -= res;
} while (res > 0 && toWrite > 0);
int max = toSend;
while (max > 0) {
S sinkChannel = pendingFrames.get(0);
PooledByteBuffer frameHeaderByteBuffer = sinkChannel.getFrameHeader().getByteBuffer();
ByteBuffer frameTrailerBuffer = sinkChannel.getFrameHeader().getTrailer();
if (frameHeaderByteBuffer != null && frameHeaderByteBuffer.getBuffer().hasRemaining() || sinkChannel.getBuffer() != null && sinkChannel.getBuffer().hasRemaining() || frameTrailerBuffer != null && frameTrailerBuffer.hasRemaining()) {
break;
}
sinkChannel.flushComplete();
pendingFrames.remove(sinkChannel);
max--;
}
if (!pendingFrames.isEmpty() || !channel.getSinkChannel().flush()) {
channel.getSinkChannel().resumeWrites();
} else {
channel.getSinkChannel().suspendWrites();
}
if (pendingFrames.isEmpty() && finalFrame) {
//all data has been sent. Close gracefully
channel.getSinkChannel().shutdownWrites();
if (!channel.getSinkChannel().flush()) {
channel.getSinkChannel().setWriteListener(ChannelListeners.flushingChannelListener(null, null));
channel.getSinkChannel().resumeWrites();
}
}
} catch (IOException e) {
safeClose(channel);
markWritesBroken(e);
}
} finally {
flushingSenders = false;
if (!newFrames.isEmpty()) {
runInIoThread(new Runnable() {
@Override
public void run() {
flushSenders();
}
});
}
}
}
use of io.undertow.connector.PooledByteBuffer in project undertow by undertow-io.
the class StringReadChannelListener method setup.
public void setup(final StreamSourceChannel channel) {
PooledByteBuffer resource = bufferPool.allocate();
ByteBuffer buffer = resource.getBuffer();
try {
int r = 0;
do {
r = channel.read(buffer);
if (r == 0) {
channel.getReadSetter().set(this);
channel.resumeReads();
} else if (r == -1) {
stringDone(string.extract());
IoUtils.safeClose(channel);
} else {
buffer.flip();
string.write(buffer);
}
} while (r > 0);
} catch (IOException e) {
error(e);
} finally {
resource.close();
}
}
Aggregations