use of io.undertow.connector.PooledByteBuffer in project undertow by undertow-io.
the class Http2DataStreamSinkChannel method createFrameHeaderImpl.
@Override
protected SendFrameHeader createFrameHeaderImpl() {
//TODO: this is a mess WRT re-using between headers and push_promise, sort out a more reasonable abstraction
int dataPaddingBytes = getChannel().getPaddingBytes();
int attempted = getBuffer().remaining() + dataPaddingBytes + (dataPaddingBytes > 0 ? 1 : 0);
final int fcWindow = grabFlowControlBytes(attempted);
if (fcWindow == 0 && getBuffer().hasRemaining()) {
//flow control window is exhausted
return new SendFrameHeader(getBuffer().remaining(), null);
}
if (fcWindow <= dataPaddingBytes + 1) {
//so we won't actually be able to send any data, just padding, which is obviously not what we want
if (getBuffer().remaining() >= fcWindow) {
//easy fix, we just don't send any data
dataPaddingBytes = 0;
} else if (getBuffer().remaining() == dataPaddingBytes) {
//corner case.
dataPaddingBytes = 1;
} else {
dataPaddingBytes = fcWindow - getBuffer().remaining() - 1;
}
}
final boolean finalFrame = isWritesShutdown() && fcWindow >= getBuffer().remaining();
PooledByteBuffer firstHeaderBuffer = getChannel().getBufferPool().allocate();
PooledByteBuffer[] allHeaderBuffers = null;
ByteBuffer firstBuffer = firstHeaderBuffer.getBuffer();
boolean firstFrame = false;
if (first) {
firstFrame = true;
first = false;
//back fill the length
firstBuffer.put((byte) 0);
firstBuffer.put((byte) 0);
firstBuffer.put((byte) 0);
//type
firstBuffer.put((byte) frameType);
//back fill the flags
firstBuffer.put((byte) 0);
Http2ProtocolUtils.putInt(firstBuffer, getStreamId());
int paddingBytes = getChannel().getPaddingBytes();
if (paddingBytes > 0) {
firstBuffer.put((byte) (paddingBytes & 0xFF));
}
writeBeforeHeaderBlock(firstBuffer);
HpackEncoder.State result = encoder.encode(headers, firstBuffer);
PooledByteBuffer current = firstHeaderBuffer;
int headerFrameLength = firstBuffer.position() - 9 + paddingBytes;
firstBuffer.put(0, (byte) ((headerFrameLength >> 16) & 0xFF));
firstBuffer.put(1, (byte) ((headerFrameLength >> 8) & 0xFF));
firstBuffer.put(2, (byte) (headerFrameLength & 0xFF));
//flags
firstBuffer.put(4, (byte) ((isWritesShutdown() && !getBuffer().hasRemaining() && frameType == Http2Channel.FRAME_TYPE_HEADERS ? Http2Channel.HEADERS_FLAG_END_STREAM : 0) | (result == HpackEncoder.State.COMPLETE ? Http2Channel.HEADERS_FLAG_END_HEADERS : 0) | (paddingBytes > 0 ? Http2Channel.HEADERS_FLAG_PADDED : 0)));
ByteBuffer currentBuffer = firstBuffer;
if (currentBuffer.remaining() < paddingBytes) {
allHeaderBuffers = allocateAll(allHeaderBuffers, current);
current = allHeaderBuffers[allHeaderBuffers.length - 1];
currentBuffer = current.getBuffer();
}
for (int i = 0; i < paddingBytes; ++i) {
currentBuffer.put((byte) 0);
}
while (result != HpackEncoder.State.COMPLETE) {
//todo: add some kind of limit here
allHeaderBuffers = allocateAll(allHeaderBuffers, current);
current = allHeaderBuffers[allHeaderBuffers.length - 1];
//continuation frame
//note that if the buffers are small we may not actually need a continuation here
//but it greatly reduces the code complexity
//back fill the length
currentBuffer = current.getBuffer();
currentBuffer.put((byte) 0);
currentBuffer.put((byte) 0);
currentBuffer.put((byte) 0);
//type
currentBuffer.put((byte) Http2Channel.FRAME_TYPE_CONTINUATION);
//back fill the flags
currentBuffer.put((byte) 0);
Http2ProtocolUtils.putInt(currentBuffer, getStreamId());
result = encoder.encode(headers, currentBuffer);
int contFrameLength = currentBuffer.position() - 9;
currentBuffer.put(0, (byte) ((contFrameLength >> 16) & 0xFF));
currentBuffer.put(1, (byte) ((contFrameLength >> 8) & 0xFF));
currentBuffer.put(2, (byte) (contFrameLength & 0xFF));
//flags
currentBuffer.put(4, (byte) (result == HpackEncoder.State.COMPLETE ? Http2Channel.HEADERS_FLAG_END_HEADERS : 0));
}
}
PooledByteBuffer currentPooled = allHeaderBuffers == null ? firstHeaderBuffer : allHeaderBuffers[allHeaderBuffers.length - 1];
ByteBuffer currentBuffer = currentPooled.getBuffer();
ByteBuffer trailer = null;
int remainingInBuffer = 0;
if (getBuffer().remaining() > 0) {
if (fcWindow > 0) {
//make sure we have room in the header buffer
if (currentBuffer.remaining() < 10) {
allHeaderBuffers = allocateAll(allHeaderBuffers, currentPooled);
currentPooled = allHeaderBuffers == null ? firstHeaderBuffer : allHeaderBuffers[allHeaderBuffers.length - 1];
currentBuffer = currentPooled.getBuffer();
}
int toSend = fcWindow - dataPaddingBytes - (dataPaddingBytes > 0 ? 1 : 0);
remainingInBuffer = getBuffer().remaining() - toSend;
getBuffer().limit(getBuffer().position() + toSend);
currentBuffer.put((byte) ((fcWindow >> 16) & 0xFF));
currentBuffer.put((byte) ((fcWindow >> 8) & 0xFF));
currentBuffer.put((byte) (fcWindow & 0xFF));
//type
currentBuffer.put((byte) Http2Channel.FRAME_TYPE_DATA);
//flags
currentBuffer.put((byte) ((finalFrame ? Http2Channel.DATA_FLAG_END_STREAM : 0) | (dataPaddingBytes > 0 ? Http2Channel.DATA_FLAG_PADDED : 0)));
Http2ProtocolUtils.putInt(currentBuffer, getStreamId());
if (dataPaddingBytes > 0) {
currentBuffer.put((byte) (dataPaddingBytes & 0xFF));
trailer = ByteBuffer.allocate(dataPaddingBytes);
}
} else {
remainingInBuffer = getBuffer().remaining();
}
} else if (finalFrame && !firstFrame) {
currentBuffer.put((byte) ((fcWindow >> 16) & 0xFF));
currentBuffer.put((byte) ((fcWindow >> 8) & 0xFF));
currentBuffer.put((byte) (fcWindow & 0xFF));
//type
currentBuffer.put((byte) Http2Channel.FRAME_TYPE_DATA);
//flags
currentBuffer.put((byte) ((Http2Channel.HEADERS_FLAG_END_STREAM & 0xFF) | (dataPaddingBytes > 0 ? Http2Channel.DATA_FLAG_PADDED : 0)));
Http2ProtocolUtils.putInt(currentBuffer, getStreamId());
if (dataPaddingBytes > 0) {
currentBuffer.put((byte) (dataPaddingBytes & 0xFF));
trailer = ByteBuffer.allocate(dataPaddingBytes);
}
}
if (allHeaderBuffers == null) {
//only one buffer required
currentBuffer.flip();
return new SendFrameHeader(remainingInBuffer, currentPooled, false, trailer);
} else {
//headers were too big to fit in one buffer
//for now we will just copy them into a big buffer
int length = 0;
for (int i = 0; i < allHeaderBuffers.length; ++i) {
length += allHeaderBuffers[i].getBuffer().position();
allHeaderBuffers[i].getBuffer().flip();
}
try {
ByteBuffer newBuf = ByteBuffer.allocate(length);
for (int i = 0; i < allHeaderBuffers.length; ++i) {
newBuf.put(allHeaderBuffers[i].getBuffer());
}
newBuf.flip();
return new SendFrameHeader(remainingInBuffer, new ImmediatePooledByteBuffer(newBuf), false, trailer);
} finally {
//the allocate can oome
for (int i = 0; i < allHeaderBuffers.length; ++i) {
allHeaderBuffers[i].close();
}
}
}
}
use of io.undertow.connector.PooledByteBuffer in project undertow by undertow-io.
the class BlockingReceiverImpl method receivePartialBytes.
@Override
public void receivePartialBytes(final PartialBytesCallback callback, final ErrorCallback errorCallback) {
if (done) {
throw UndertowMessages.MESSAGES.requestBodyAlreadyRead();
}
final ErrorCallback error = errorCallback == null ? END_EXCHANGE : errorCallback;
if (callback == null) {
throw UndertowMessages.MESSAGES.argumentCannotBeNull("callback");
}
if (exchange.isRequestComplete()) {
callback.handle(exchange, EMPTY_BYTE_ARRAY, true);
return;
}
String contentLengthString = exchange.getRequestHeaders().getFirst(Headers.CONTENT_LENGTH);
long contentLength;
if (contentLengthString != null) {
contentLength = Long.parseLong(contentLengthString);
if (contentLength > Integer.MAX_VALUE) {
error.error(exchange, new RequestToLargeException());
return;
}
} else {
contentLength = -1;
}
if (maxBufferSize > 0) {
if (contentLength > maxBufferSize) {
error.error(exchange, new RequestToLargeException());
return;
}
}
int s;
try (PooledByteBuffer pooled = exchange.getConnection().getByteBufferPool().getArrayBackedPool().allocate()) {
while ((s = inputStream.read(pooled.getBuffer().array(), pooled.getBuffer().arrayOffset(), pooled.getBuffer().remaining())) > 0) {
byte[] newData = new byte[s];
System.arraycopy(pooled.getBuffer().array(), pooled.getBuffer().arrayOffset(), newData, 0, s);
callback.handle(exchange, newData, false);
}
callback.handle(exchange, EMPTY_BYTE_ARRAY, true);
} catch (IOException e) {
error.error(exchange, e);
}
}
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 AjpClientRequestClientStreamSinkChannel method createFrameHeaderImpl.
private SendFrameHeader createFrameHeaderImpl() {
if (discardMode) {
getBuffer().clear();
getBuffer().flip();
return new SendFrameHeader(new ImmediatePooledByteBuffer(ByteBuffer.wrap(new byte[0])));
}
PooledByteBuffer pooledHeaderBuffer = getChannel().getBufferPool().allocate();
try {
final ByteBuffer buffer = pooledHeaderBuffer.getBuffer();
ByteBuffer dataBuffer = getBuffer();
int dataInBuffer = dataBuffer.remaining();
if (!firstFrameWritten && requestedChunkSize == 0) {
//we are waiting on a read body chunk
return new SendFrameHeader(dataInBuffer, null);
}
int maxData = getChannel().getSettings().get(UndertowOptions.MAX_AJP_PACKET_SIZE, DEFAULT_MAX_DATA_SIZE) - 6;
if (!firstFrameWritten) {
String contentLength = headers.getFirst(Headers.CONTENT_LENGTH);
if (contentLength != null) {
dataSize = Long.parseLong(contentLength);
requestedChunkSize = maxData;
if (dataInBuffer > dataSize) {
throw UndertowMessages.MESSAGES.fixedLengthOverflow();
}
} else if (isWritesShutdown() && !headers.contains(Headers.TRANSFER_ENCODING)) {
//writes are shut down, go to fixed length
headers.put(Headers.CONTENT_LENGTH, dataInBuffer);
dataSize = dataInBuffer;
requestedChunkSize = maxData;
} else {
headers.put(Headers.TRANSFER_ENCODING, Headers.CHUNKED.toString());
dataSize = -1;
requestedChunkSize = 0;
}
firstFrameWritten = true;
final String path;
final String queryString;
int qsIndex = this.path.indexOf('?');
if (qsIndex == -1) {
path = this.path;
queryString = null;
} else {
path = this.path.substring(0, qsIndex);
queryString = this.path.substring(qsIndex + 1);
}
buffer.put((byte) 0x12);
buffer.put((byte) 0x34);
//we fill the size in later
buffer.put((byte) 0);
buffer.put((byte) 0);
buffer.put((byte) 2);
boolean storeMethod = false;
Integer methodNp = AjpConstants.HTTP_METHODS_MAP.get(method);
if (methodNp == null) {
methodNp = 0xFF;
storeMethod = true;
}
buffer.put((byte) (int) methodNp);
AjpUtils.putHttpString(buffer, protocol);
putString(buffer, path);
putString(buffer, notNull(attachable.getAttachment(ProxiedRequestAttachments.REMOTE_ADDRESS)));
putString(buffer, notNull(attachable.getAttachment(ProxiedRequestAttachments.REMOTE_HOST)));
putString(buffer, notNull(attachable.getAttachment(ProxiedRequestAttachments.SERVER_NAME)));
AjpUtils.putInt(buffer, notNull(attachable.getAttachment(ProxiedRequestAttachments.SERVER_PORT)));
buffer.put((byte) (notNull(attachable.getAttachment(ProxiedRequestAttachments.IS_SSL)) ? 1 : 0));
int headers = 0;
//we need to count the headers
final HeaderMap responseHeaders = this.headers;
for (HttpString name : responseHeaders.getHeaderNames()) {
headers += responseHeaders.get(name).size();
}
AjpUtils.putInt(buffer, headers);
for (final HttpString header : responseHeaders.getHeaderNames()) {
for (String headerValue : responseHeaders.get(header)) {
Integer headerCode = AjpConstants.HEADER_MAP.get(header);
if (headerCode != null) {
AjpUtils.putInt(buffer, headerCode);
} else {
AjpUtils.putHttpString(buffer, header);
}
putString(buffer, headerValue);
}
}
if (queryString != null) {
//query_string
buffer.put((byte) ATTR_QUERY_STRING);
putString(buffer, queryString);
}
String remoteUser = attachable.getAttachment(ProxiedRequestAttachments.REMOTE_USER);
if (remoteUser != null) {
buffer.put((byte) ATTR_REMOTE_USER);
putString(buffer, remoteUser);
}
String authType = attachable.getAttachment(ProxiedRequestAttachments.AUTH_TYPE);
if (authType != null) {
buffer.put((byte) ATTR_AUTH_TYPE);
putString(buffer, authType);
}
String route = attachable.getAttachment(ProxiedRequestAttachments.ROUTE);
if (route != null) {
buffer.put((byte) ATTR_ROUTE);
putString(buffer, route);
}
String sslCert = attachable.getAttachment(ProxiedRequestAttachments.SSL_CERT);
if (sslCert != null) {
buffer.put((byte) ATTR_SSL_CERT);
putString(buffer, sslCert);
}
String sslCypher = attachable.getAttachment(ProxiedRequestAttachments.SSL_CYPHER);
if (sslCypher != null) {
buffer.put((byte) ATTR_SSL_CIPHER);
putString(buffer, sslCypher);
}
byte[] sslSession = attachable.getAttachment(ProxiedRequestAttachments.SSL_SESSION_ID);
if (sslSession != null) {
buffer.put((byte) ATTR_SSL_SESSION);
putString(buffer, FlexBase64.encodeString(sslSession, false));
}
Integer sslKeySize = attachable.getAttachment(ProxiedRequestAttachments.SSL_KEY_SIZE);
if (sslKeySize != null) {
buffer.put((byte) ATTR_SSL_KEY_SIZE);
putString(buffer, sslKeySize.toString());
}
String secret = attachable.getAttachment(ProxiedRequestAttachments.SECRET);
if (secret != null) {
buffer.put((byte) ATTR_SECRET);
putString(buffer, secret);
}
if (storeMethod) {
buffer.put((byte) ATTR_STORED_METHOD);
putString(buffer, method.toString());
}
buffer.put((byte) 0xFF);
int dataLength = buffer.position() - 4;
buffer.put(2, (byte) ((dataLength >> 8) & 0xFF));
buffer.put(3, (byte) (dataLength & 0xFF));
}
if (dataSize == 0) {
//no data, just write out this frame and we are done
buffer.flip();
return new SendFrameHeader(pooledHeaderBuffer);
} else if (requestedChunkSize > 0) {
if (isWritesShutdown() && dataInBuffer == 0) {
buffer.put((byte) 0x12);
buffer.put((byte) 0x34);
buffer.put((byte) 0x00);
buffer.put((byte) 0x02);
buffer.put((byte) 0x00);
buffer.put((byte) 0x00);
buffer.flip();
return new SendFrameHeader(pooledHeaderBuffer);
}
int remaining = dataInBuffer;
remaining = Math.min(remaining, maxData);
remaining = Math.min(remaining, requestedChunkSize);
int bodySize = remaining + 2;
buffer.put((byte) 0x12);
buffer.put((byte) 0x34);
buffer.put((byte) ((bodySize >> 8) & 0xFF));
buffer.put((byte) (bodySize & 0xFF));
buffer.put((byte) ((remaining >> 8) & 0xFF));
buffer.put((byte) (remaining & 0xFF));
requestedChunkSize = 0;
if (remaining < dataInBuffer) {
dataBuffer.limit(getBuffer().position() + remaining);
buffer.flip();
return new SendFrameHeader(dataInBuffer - remaining, pooledHeaderBuffer, dataSize < 0);
} else {
buffer.flip();
return new SendFrameHeader(0, pooledHeaderBuffer, dataSize < 0);
}
} else {
//chunked. We just write the headers, and leave all the data in the buffer
//they need to send us a read body chunk in order to get any data
buffer.flip();
if (buffer.remaining() == 0) {
pooledHeaderBuffer.close();
return new SendFrameHeader(dataInBuffer, null, true);
}
dataBuffer.limit(dataBuffer.position());
return new SendFrameHeader(dataInBuffer, pooledHeaderBuffer, true);
}
} catch (BufferOverflowException e) {
//TODO: UNDERTOW-901
pooledHeaderBuffer.close();
markBroken();
throw e;
}
}
use of io.undertow.connector.PooledByteBuffer in project undertow by undertow-io.
the class ReadDataStreamSourceConduit method read.
@Override
public long read(final ByteBuffer[] dsts, final int offs, final int len) throws IOException {
PooledByteBuffer eb = connection.getExtraBytes();
if (eb != null) {
final ByteBuffer buffer = eb.getBuffer();
int result = Buffers.copy(dsts, offs, len, buffer);
if (!buffer.hasRemaining()) {
eb.close();
connection.setExtraBytes(null);
}
return result;
} else {
return super.read(dsts, offs, len);
}
}
Aggregations