@Override public void handleEvent(StreamSourceChannel channel) { if (this.subscriptionClosed) { return; } try { ByteBuffer buffer = this.pooledBuffer.getBuffer(); int count; do { count = channel.read(buffer); if (count == 0) { return; } else if (count == -1) { if (buffer.position() > 0) { doOnNext(buffer); } doOnComplete(); } else { if (buffer.remaining() == 0) { if (this.demand == 0) { channel.suspendReads(); } doOnNext(buffer); if (this.demand > 0) { scheduleNextMessage(); } break; } } } while (count > 0); } catch (IOException e) { doOnError(e); } }
@Override public int read(final byte[] b, final int off, final int len) throws IOException { if (anyAreSet(state, FLAG_CLOSED)) { throw UndertowServletMessages.MESSAGES.streamIsClosed(); } if (anyAreSet(state, FLAG_FINISHED)) { return -1; } ByteBuffer buffer = ByteBuffer.wrap(b, off, len); if (listener == null) { int res = Channels.readBlocking(channel, buffer); if (res == -1) { state |= FLAG_FINISHED; } return res; } else { if (anyAreClear(state, FLAG_READY)) { throw UndertowServletMessages.MESSAGES.streamNotReady(); } int res = channel.read(buffer); if (res == -1) { state |= FLAG_FINISHED; } else if (res == 0) { state &= ~FLAG_READY; channel.resumeReads(); } return res; } }
void cleanup() { // All other cleanup handlers have been called. We will inspect the state of the exchange // and attempt to fix any leftover or broken crap as best as we can. // // At this point if any channels were not acquired, we know that not even default handlers have // handled the request, meaning we basically have no idea what their state is; the response // headers // may not even be valid. // // The only thing we can do is to determine if the request and reply were both terminated; if // not, // consume the request body nicely, send whatever HTTP response we have, and close down the // connection. complete = true; int oldVal, newVal; do { oldVal = state; if (allAreSet(oldVal, FLAG_CLEANUP)) { return; } newVal = oldVal | FLAG_CLEANUP | FLAG_REQUEST_TERMINATED | FLAG_RESPONSE_TERMINATED; } while (!stateUpdater.compareAndSet(this, oldVal, newVal)); final StreamSourceChannel requestChannel = underlyingRequestChannel; final StreamSinkChannel responseChannel = underlyingResponseChannel; if (allAreSet(oldVal, FLAG_REQUEST_TERMINATED | FLAG_RESPONSE_TERMINATED)) { // we're good; a transfer coding handler took care of things. return; } else { try { // we do not attempt to drain the read side, as one of the reasons this could // be happening is because the request was too large requestChannel.shutdownReads(); responseChannel.shutdownWrites(); if (!responseChannel.flush()) { responseChannel .getWriteSetter() .set( ChannelListeners.<StreamSinkChannel>flushingChannelListener( new ChannelListener<StreamSinkChannel>() { public void handleEvent(final StreamSinkChannel channel) { // this shouldn't be necessary... channel.suspendWrites(); channel.getWriteSetter().set(null); } }, ChannelListeners.closingChannelExceptionHandler())); responseChannel.resumeWrites(); } } catch (Throwable t) { // All sorts of things could go wrong, from runtime exceptions to java.io.IOException to // errors. // Just kill off the connection, it's f****d beyond repair. safeClose(requestChannel); safeClose(responseChannel); safeClose(connection); } } }
@Override public void setReadListener(final ReadListener readListener) { if (readListener == null) { throw UndertowServletMessages.MESSAGES.paramCannotBeNull("readListener"); } if (listener != null) { throw UndertowServletMessages.MESSAGES.listenerAlreadySet(); } listener = readListener; channel.getReadSetter().set(new UpgradeServletChannelListener()); channel.resumeReads(); }
@Override public FormData parseBlocking() throws IOException { final FormData existing = exchange.getAttachment(FORM_DATA); if (existing != null) { return existing; } StreamSourceChannel channel = exchange.getRequestChannel(); if (channel == null) { throw new IOException(UndertowMessages.MESSAGES.requestChannelAlreadyProvided()); } else { while (state != 4) { doParse(channel); if (state != 4) { channel.awaitReadable(); } } } return data; }
@Override public void parse(HttpHandler handler) throws Exception { if (exchange.getAttachment(FORM_DATA) != null) { handler.handleRequest(exchange); return; } this.handler = handler; StreamSourceChannel channel = exchange.getRequestChannel(); if (channel == null) { throw new IOException(UndertowMessages.MESSAGES.requestChannelAlreadyProvided()); } else { doParse(channel); if (state != 4) { channel.getReadSetter().set(this); channel.resumeReads(); } else { exchange.dispatch(SameThreadExecutor.INSTANCE, handler); } } }
@Override public void handleEvent(final StreamSourceChannel channel) { if (anyAreClear(state, FLAG_FINISHED)) { try { state |= FLAG_READY; channel.suspendReads(); listener.onDataAvailable(); } catch (IOException e) { UndertowLogger.REQUEST_IO_LOGGER.ioException(e); IoUtils.safeClose(channel); } } if (anyAreSet(state, FLAG_FINISHED) && anyAreClear(state, FLAG_ON_DATA_READ_CALLED)) { state |= FLAG_ON_DATA_READ_CALLED; try { channel.shutdownReads(); listener.onAllDataRead(); } catch (IOException e) { IoUtils.safeClose(channel); } } else if (allAreClear(state, FLAG_FINISHED)) { channel.resumeReads(); } }
@Override public void handleEvent(StreamSourceChannel channel) { if (this.session.isDisconnected()) { if (logger.isDebugEnabled()) { logger.debug("SockJS sockJsSession closed, closing response."); } IoUtils.safeClose(this.connection); throw new SockJsException("Session closed.", this.session.getId(), null); } Object pooled = undertowBufferSupport.allocatePooledResource(); try { int r; do { ByteBuffer buffer = undertowBufferSupport.getByteBuffer(pooled); buffer.clear(); r = channel.read(buffer); buffer.flip(); if (r == 0) { return; } else if (r == -1) { onSuccess(); } else { while (buffer.hasRemaining()) { int b = buffer.get(); if (b == '\n') { handleFrame(); } else { this.outputStream.write(b); } } } } while (r > 0); } catch (IOException exc) { onFailure(exc); } finally { undertowBufferSupport.closePooledResource(pooled); } }
public void handleEvent(final Channel channel) { if (done) { if (channel instanceof StreamSinkChannel) { ((StreamSinkChannel) channel).suspendWrites(); } else if (channel instanceof StreamSourceChannel) { ((StreamSourceChannel) channel).suspendReads(); } return; } boolean noWrite = false; if (pooledBuffer == null) { pooledBuffer = pool.allocate(); noWrite = true; } else if (channel instanceof StreamSourceChannel) { noWrite = true; // attempt a read first, as this is a read notification pooledBuffer.getResource().compact(); } final ByteBuffer buffer = pooledBuffer.getResource(); try { long read; for (; ; ) { boolean writeFailed = false; // always attempt to write first if we have the buffer if (!noWrite) { while (buffer.hasRemaining()) { final int res; try { res = sink.write(buffer); } catch (IOException e) { pooledBuffer.free(); pooledBuffer = null; done = true; ChannelListeners.invokeChannelExceptionHandler(sink, writeExceptionHandler, e); return; } if (res == 0) { writeFailed = true; break; } } if (sourceDone && !buffer.hasRemaining()) { done = true; done(source, sink, sourceListener, sinkListener); return; } buffer.compact(); } noWrite = false; if (buffer.hasRemaining() && !sourceDone) { try { read = source.read(buffer); buffer.flip(); } catch (IOException e) { pooledBuffer.free(); pooledBuffer = null; done = true; ChannelListeners.invokeChannelExceptionHandler(source, readExceptionHandler, e); return; } if (read == 0) { break; } else if (read == -1) { sourceDone = true; if (!buffer.hasRemaining()) { done = true; done(source, sink, sourceListener, sinkListener); return; } } } else { buffer.flip(); if (writeFailed) { break; } } } // suspend writes if there is nothing to write if (!buffer.hasRemaining()) { sink.suspendWrites(); } else if (!sink.isWriteResumed()) { sink.resumeWrites(); } // suspend reads if there is nothing to read if (buffer.remaining() == buffer.capacity()) { source.suspendReads(); } else if (!source.isReadResumed()) { source.resumeReads(); } } finally { if (pooledBuffer != null && !buffer.hasRemaining()) { pooledBuffer.free(); pooledBuffer = null; } } }
public XnioExecutor getReadThread() { return underlyingRequestChannel.getReadThread(); }
public void handleEvent(final StreamSourceChannel channel) { Pooled<ByteBuffer> existing = connection.getExtraBytes(); final Pooled<ByteBuffer> pooled = existing == null ? connection.getBufferPool().allocate() : existing; final ByteBuffer buffer = pooled.getResource(); boolean free = true; try { int res; 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 (!channel.isReadResumed()) { channel.getReadSetter().set(this); channel.resumeReads(); } return; } else if (res == -1) { try { channel.suspendReads(); channel.shutdownReads(); final StreamSinkChannel responseChannel = this.connection.getChannel().getSinkChannel(); responseChannel.shutdownWrites(); // will return false if there's a response queued ahead of this one, so we'll set up a // listener then if (!responseChannel.flush()) { responseChannel .getWriteSetter() .set(ChannelListeners.flushingChannelListener(null, null)); responseChannel.resumeWrites(); } } catch (IOException e) { UndertowLogger.REQUEST_IO_LOGGER.debug("Error reading request", e); // f**k it, it's all ruined IoUtils.safeClose(channel); return; } return; } if (existing != null) { existing = null; connection.setExtraBytes(null); } else { buffer.flip(); } parser.handle(buffer, state, httpServerExchange); if (buffer.hasRemaining()) { free = false; connection.setExtraBytes(pooled); } int total = read + res; read = total; if (read > maxRequestSize) { UndertowLogger.REQUEST_LOGGER.requestHeaderWasTooLarge( connection.getPeerAddress(), maxRequestSize); IoUtils.safeClose(connection); return; } } while (!state.isComplete()); // 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; httpServerExchange.putAttachment( UndertowOptions.ATTACHMENT_KEY, connection.getUndertowOptions()); httpServerExchange.setRequestScheme(connection.getSslSession() != null ? "https" : "http"); this.httpServerExchange = null; HttpTransferEncoding.setupRequest(httpServerExchange); HttpHandlers.executeRootHandler( connection.getRootHandler(), httpServerExchange, Thread.currentThread() instanceof XnioExecutor); } catch (Exception e) { sendBadRequestAndClose(connection.getChannel(), e); return; } finally { if (free) pooled.free(); } }
@Override public void close() throws IOException { channel.shutdownReads(); state |= FLAG_FINISHED | FLAG_CLOSED; }
private void doParse(final StreamSourceChannel channel) throws IOException { int c = 0; final Pooled<ByteBuffer> pooled = exchange.getConnection().getBufferPool().allocate(); try { final ByteBuffer buffer = pooled.getResource(); do { buffer.clear(); c = channel.read(buffer); if (c > 0) { buffer.flip(); while (buffer.hasRemaining()) { byte n = buffer.get(); switch (state) { case 0: { if (n == '=') { name = builder.toString(); builder.setLength(0); state = 2; } else if (n == '&') { data.add(builder.toString(), ""); builder.setLength(0); state = 0; } else if (n == '%' || n == '+') { state = 1; builder.append((char) n); } else { builder.append((char) n); } break; } case 1: { if (n == '=') { name = URLDecoder.decode(builder.toString(), charset); builder.setLength(0); state = 2; } else if (n == '&') { data.add(URLDecoder.decode(builder.toString(), charset), ""); builder.setLength(0); state = 0; } else { builder.append((char) n); } break; } case 2: { if (n == '&') { data.add(name, builder.toString()); builder.setLength(0); state = 0; } else if (n == '%' || n == '+') { state = 3; builder.append((char) n); } else { builder.append((char) n); } break; } case 3: { if (n == '&') { data.add(name, URLDecoder.decode(builder.toString(), charset)); builder.setLength(0); state = 0; } else { builder.append((char) n); } break; } } } } } while (c > 0); if (c == -1) { if (state == 2) { data.add(name, builder.toString()); } else if (state == 3) { data.add(name, URLDecoder.decode(builder.toString(), charset)); } else if (builder.length() > 0) { if (state == 1) { data.add(URLDecoder.decode(builder.toString(), charset), ""); } else { data.add(builder.toString(), ""); } } state = 4; exchange.putAttachment(FORM_DATA, data); } } finally { pooled.free(); } }
public void handleEvent(StreamSourceChannel channel) { HttpResponseBuilder builder = pendingResponse; final Pooled<ByteBuffer> pooled = bufferPool.allocate(); final ByteBuffer buffer = pooled.getResource(); boolean free = true; try { if (builder == null) { // read ready when no request pending buffer.clear(); try { int res = channel.read(buffer); if (res == -1) { UndertowLogger.CLIENT_LOGGER.debugf( "Connection to %s was closed by the target server", connection.getPeerAddress()); IoUtils.safeClose(HttpClientConnection.this); } else if (res != 0) { UndertowLogger.CLIENT_LOGGER.debugf( "Target server %s sent unexpected data when no request pending, closing connection", connection.getPeerAddress()); IoUtils.safeClose(HttpClientConnection.this); } // otherwise it is a spurious notification } catch (IOException e) { if (UndertowLogger.CLIENT_LOGGER.isDebugEnabled()) { UndertowLogger.CLIENT_LOGGER.debugf(e, "Connection closed with IOException"); } safeClose(connection); } return; } final ResponseParseState state = builder.getParseState(); int res; do { buffer.clear(); try { res = channel.read(buffer); } catch (IOException e) { if (UndertowLogger.CLIENT_LOGGER.isDebugEnabled()) { UndertowLogger.CLIENT_LOGGER.debugf(e, "Connection closed with IOException"); } safeClose(channel); return; } if (res == 0) { if (!channel.isReadResumed()) { channel.getReadSetter().set(this); channel.resumeReads(); } return; } else if (res == -1) { channel.suspendReads(); IoUtils.safeClose(HttpClientConnection.this); try { final StreamSinkChannel requestChannel = connection.getSinkChannel(); requestChannel.shutdownWrites(); // will return false if there's a response queued ahead of this one, so we'll set up a // listener then if (!requestChannel.flush()) { requestChannel .getWriteSetter() .set(ChannelListeners.flushingChannelListener(null, null)); requestChannel.resumeWrites(); } // Cancel the current active request currentRequest.setFailed(new IOException(MESSAGES.connectionClosed())); } catch (IOException e) { if (UndertowLogger.CLIENT_LOGGER.isDebugEnabled()) { UndertowLogger.CLIENT_LOGGER.debugf( e, "Connection closed with IOException when attempting to shut down reads"); } IoUtils.safeClose(channel); // Cancel the current active request currentRequest.setFailed(e); return; } return; } buffer.flip(); HttpResponseParser.INSTANCE.handle(buffer, state, builder); if (buffer.hasRemaining()) { free = false; pushBackStreamSourceConduit.pushBack(pooled); } } while (!state.isComplete()); final ClientResponse response = builder.build(); String connectionString = response.getResponseHeaders().getFirst(CONNECTION); // check if an upgrade worked if (anyAreSet(HttpClientConnection.this.state, UPGRADE_REQUESTED)) { if ((connectionString == null || !UPGRADE.equalToString(connectionString)) && !response.getResponseHeaders().contains(UPGRADE)) { // just unset the upgrade requested flag HttpClientConnection.this.state &= ~UPGRADE_REQUESTED; } } if (connectionString != null) { if (HttpString.tryFromString(connectionString).equals(Headers.CLOSE)) { HttpClientConnection.this.state |= CLOSE_REQ; } } if (builder.getStatusCode() == 100) { pendingResponse = new HttpResponseBuilder(); currentRequest.setContinueResponse(response); } else { prepareResponseChannel(response, currentRequest); channel.getReadSetter().set(null); channel.suspendReads(); pendingResponse = null; currentRequest.setResponse(response); } } catch (Exception e) { UndertowLogger.CLIENT_LOGGER.exceptionProcessingRequest(e); IoUtils.safeClose(connection); currentRequest.setFailed(new IOException(e)); } finally { if (free) pooled.free(); } }
public void setup(StreamSourceChannel channel) { channel.suspendReads(); channel.getReadSetter().set(this); channel.resumeReads(); }