use of io.undertow.connector.PooledByteBuffer in project undertow by undertow-io.
the class BlockingSenderImpl method performTransfer.
private void performTransfer(FileChannel source, IoCallback callback) {
if (outputStream instanceof BufferWritableOutputStream) {
try {
((BufferWritableOutputStream) outputStream).transferFrom(source);
} catch (IOException e) {
callback.onException(exchange, this, e);
}
} else {
try (PooledByteBuffer pooled = exchange.getConnection().getByteBufferPool().getArrayBackedPool().allocate()) {
ByteBuffer buffer = pooled.getBuffer();
long pos = source.position();
long size = source.size();
while (size - pos > 0) {
int ret = source.read(buffer);
if (ret <= 0) {
break;
}
pos += ret;
outputStream.write(buffer.array(), buffer.arrayOffset(), ret);
buffer.clear();
}
if (pos != size) {
throw new EOFException("Unexpected EOF reading file");
}
} catch (IOException e) {
callback.onException(exchange, this, e);
}
}
}
use of io.undertow.connector.PooledByteBuffer in project undertow by undertow-io.
the class ChunkedStreamSinkConduit method createLastChunk.
private void createLastChunk(final boolean writeFinal) throws UnsupportedEncodingException {
PooledByteBuffer lastChunkBufferPooled = bufferPool.allocate();
ByteBuffer lastChunkBuffer = lastChunkBufferPooled.getBuffer();
if (writeFinal) {
lastChunkBuffer.put(CRLF);
} else if (chunkingSepBuffer.hasRemaining()) {
// the end of chunk /r/n has not been written yet
// just add it to this buffer to make managing state easier
lastChunkBuffer.put(chunkingSepBuffer);
}
lastChunkBuffer.put(LAST_CHUNK);
// we just assume it will fit
HeaderMap attachment = attachable.getAttachment(HttpAttachments.RESPONSE_TRAILERS);
final HeaderMap trailers;
Supplier<HeaderMap> supplier = attachable.getAttachment(HttpAttachments.RESPONSE_TRAILER_SUPPLIER);
if (attachment != null && supplier == null) {
trailers = attachment;
} else if (attachment == null && supplier != null) {
trailers = supplier.get();
} else if (attachment != null) {
HeaderMap supplied = supplier.get();
for (HeaderValues k : supplied) {
attachment.putAll(k.getHeaderName(), k);
}
trailers = attachment;
} else {
trailers = null;
}
if (trailers != null && trailers.size() != 0) {
for (HeaderValues trailer : trailers) {
for (String val : trailer) {
trailer.getHeaderName().appendTo(lastChunkBuffer);
lastChunkBuffer.put((byte) ':');
lastChunkBuffer.put((byte) ' ');
lastChunkBuffer.put(val.getBytes(StandardCharsets.US_ASCII));
lastChunkBuffer.put(CRLF);
}
}
lastChunkBuffer.put(CRLF);
} else {
lastChunkBuffer.put(CRLF);
}
// horrible hack
// there is a situation where we can get a buffer leak here if the connection is terminated abnormaly
// this should be fixed once this channel has its lifecycle tied to the connection, same as fixed length
lastChunkBuffer.flip();
ByteBuffer data = ByteBuffer.allocate(lastChunkBuffer.remaining());
data.put(lastChunkBuffer);
data.flip();
this.lastChunkBuffer = new ImmediatePooledByteBuffer(data);
lastChunkBufferPooled.close();
}
use of io.undertow.connector.PooledByteBuffer in project undertow by undertow-io.
the class AjpServerResponseConduit method processAJPHeader.
/**
* Handles generating the header if required, and adding it to the frame queue.
*
* No attempt is made to actually flush this, so a gathering write can be used to actually flush the data
*/
private void processAJPHeader() {
int oldState = this.state;
if (anyAreSet(oldState, FLAG_START)) {
PooledByteBuffer[] byteBuffers = null;
// merge the cookies into the header map
Connectors.flattenCookies(exchange);
PooledByteBuffer pooled = pool.allocate();
ByteBuffer buffer = pooled.getBuffer();
buffer.put((byte) 'A');
buffer.put((byte) 'B');
// we fill the size in later
buffer.put((byte) 0);
buffer.put((byte) 0);
buffer.put((byte) 4);
putInt(buffer, exchange.getStatusCode());
String reason = exchange.getReasonPhrase();
if (reason == null) {
reason = StatusCodes.getReason(exchange.getStatusCode());
}
if (reason.length() + 4 > buffer.remaining()) {
pooled.close();
throw UndertowMessages.MESSAGES.reasonPhraseToLargeForBuffer(reason);
}
putString(buffer, reason);
int headers = 0;
// we need to count the headers
final HeaderMap responseHeaders = exchange.getResponseHeaders();
for (HttpString name : responseHeaders.getHeaderNames()) {
headers += responseHeaders.get(name).size();
}
putInt(buffer, headers);
for (final HttpString header : responseHeaders.getHeaderNames()) {
for (String headerValue : responseHeaders.get(header)) {
if (buffer.remaining() < header.length() + headerValue.length() + 6) {
// if there is not enough room in the buffer we need to allocate more
buffer.flip();
if (byteBuffers == null) {
byteBuffers = new PooledByteBuffer[2];
byteBuffers[0] = pooled;
} else {
PooledByteBuffer[] old = byteBuffers;
byteBuffers = new PooledByteBuffer[old.length + 1];
System.arraycopy(old, 0, byteBuffers, 0, old.length);
}
pooled = pool.allocate();
byteBuffers[byteBuffers.length - 1] = pooled;
buffer = pooled.getBuffer();
}
Integer headerCode = HEADER_MAP.get(header);
if (headerCode != null) {
putInt(buffer, headerCode);
} else {
putHttpString(buffer, header);
}
putString(buffer, headerValue);
}
}
if (byteBuffers == null) {
int dataLength = buffer.position() - 4;
buffer.put(2, (byte) ((dataLength >> 8) & 0xFF));
buffer.put(3, (byte) (dataLength & 0xFF));
buffer.flip();
queueFrame(new PooledBufferFrameCallback(pooled), buffer);
} else {
ByteBuffer[] bufs = new ByteBuffer[byteBuffers.length];
for (int i = 0; i < bufs.length; ++i) {
bufs[i] = byteBuffers[i].getBuffer();
}
int dataLength = (int) (Buffers.remaining(bufs) - 4);
bufs[0].put(2, (byte) ((dataLength >> 8) & 0xFF));
bufs[0].put(3, (byte) (dataLength & 0xFF));
buffer.flip();
queueFrame(new PooledBuffersFrameCallback(byteBuffers), bufs);
}
state &= ~FLAG_START;
}
}
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);
}
}
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();
res = channel.read(buffer);
} 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) {
channel.shutdownReads();
final StreamSinkChannel responseChannel = connection.getChannel().getSinkChannel();
responseChannel.shutdownWrites();
safeClose(connection);
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);
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 (!Connectors.areRequestHeadersValid(httpServerExchange.getRequestHeaders())) {
oldState.badRequest = true;
UndertowLogger.REQUEST_IO_LOGGER.debugf("Invalid AJP request from %s, request contained invalid headers", connection.getPeerAddress());
}
if (oldState.badRequest) {
httpServerExchange.setStatusCode(StatusCodes.BAD_REQUEST);
httpServerExchange.endExchange();
handleBadRequest();
safeClose(connection);
} else {
Connectors.executeRootHandler(connection.getRootHandler(), httpServerExchange);
}
} catch (BadRequestException e) {
UndertowLogger.REQUEST_IO_LOGGER.failedToParseRequest(e);
handleBadRequest();
safeClose(connection);
} catch (IOException e) {
UndertowLogger.REQUEST_IO_LOGGER.ioException(e);
handleInternalServerError();
safeClose(connection);
} catch (Throwable t) {
UndertowLogger.REQUEST_LOGGER.exceptionProcessingRequest(t);
handleInternalServerError();
safeClose(connection);
} finally {
if (free)
pooled.close();
}
}
Aggregations