Search in sources :

Example 41 with PooledByteBuffer

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);
        }
    }
}
Also used : ClosedChannelException(java.nio.channels.ClosedChannelException) SSLEngineResult(javax.net.ssl.SSLEngineResult) PooledByteBuffer(io.undertow.connector.PooledByteBuffer) InterruptedIOException(java.io.InterruptedIOException) IOException(java.io.IOException) ByteBuffer(java.nio.ByteBuffer) PooledByteBuffer(io.undertow.connector.PooledByteBuffer)

Example 42 with PooledByteBuffer

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);
}
Also used : PooledByteBuffer(io.undertow.connector.PooledByteBuffer) PooledByteBuffer(io.undertow.connector.PooledByteBuffer) ByteBuffer(java.nio.ByteBuffer) SendFrameHeader(io.undertow.server.protocol.framed.SendFrameHeader)

Example 43 with PooledByteBuffer

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();
    }
}
Also used : StreamSinkChannel(org.xnio.channels.StreamSinkChannel) ConduitStreamSinkChannel(org.xnio.conduits.ConduitStreamSinkChannel) IOException(java.io.IOException) ByteBuffer(java.nio.ByteBuffer) PooledByteBuffer(io.undertow.connector.PooledByteBuffer) IOException(java.io.IOException) HttpServerExchange(io.undertow.server.HttpServerExchange) PooledByteBuffer(io.undertow.connector.PooledByteBuffer)

Example 44 with PooledByteBuffer

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);
    }
}
Also used : ClosedChannelException(java.nio.channels.ClosedChannelException) PooledByteBuffer(io.undertow.connector.PooledByteBuffer) ByteBuffer(java.nio.ByteBuffer) PooledByteBuffer(io.undertow.connector.PooledByteBuffer)

Example 45 with PooledByteBuffer

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);
    }
}
Also used : ClosedChannelException(java.nio.channels.ClosedChannelException) PooledByteBuffer(io.undertow.connector.PooledByteBuffer) IOException(java.io.IOException) ByteBuffer(java.nio.ByteBuffer) PooledByteBuffer(io.undertow.connector.PooledByteBuffer)

Aggregations

PooledByteBuffer (io.undertow.connector.PooledByteBuffer)54 ByteBuffer (java.nio.ByteBuffer)45 IOException (java.io.IOException)28 ImmediatePooledByteBuffer (io.undertow.util.ImmediatePooledByteBuffer)9 StreamSourceChannel (org.xnio.channels.StreamSourceChannel)8 HttpServerExchange (io.undertow.server.HttpServerExchange)7 ClosedChannelException (java.nio.channels.ClosedChannelException)6 ByteArrayOutputStream (java.io.ByteArrayOutputStream)5 ChannelListener (org.xnio.ChannelListener)5 HttpHandler (io.undertow.server.HttpHandler)4 StreamSinkChannel (org.xnio.channels.StreamSinkChannel)4 SendFrameHeader (io.undertow.server.protocol.framed.SendFrameHeader)3 HeaderMap (io.undertow.util.HeaderMap)3 HttpString (io.undertow.util.HttpString)3 InterruptedIOException (java.io.InterruptedIOException)3 ByteBufferPool (io.undertow.connector.ByteBufferPool)2 CharBuffer (java.nio.CharBuffer)2 CharsetDecoder (java.nio.charset.CharsetDecoder)2 IoCallback (io.undertow.io.IoCallback)1 Sender (io.undertow.io.Sender)1