io.undertow.server.AbstractServerConnection Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source.
* Copyright 2014 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.undertow.server;
import io.undertow.UndertowLogger;
import io.undertow.UndertowMessages;
import org.xnio.ChannelListener;
import org.xnio.ChannelListeners;
import org.xnio.Option;
import org.xnio.OptionMap;
import io.undertow.connector.ByteBufferPool;
import io.undertow.connector.PooledByteBuffer;
import org.xnio.Pool;
import org.xnio.StreamConnection;
import org.xnio.XnioIoThread;
import org.xnio.XnioWorker;
import org.xnio.conduits.ConduitStreamSinkChannel;
import org.xnio.conduits.ConduitStreamSourceChannel;
import org.xnio.conduits.StreamSinkConduit;
import org.xnio.conduits.StreamSourceConduit;
import java.io.IOException;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.util.LinkedList;
import java.util.List;
public abstract class AbstractServerConnection extends ServerConnection {
protected final StreamConnection channel;
protected final CloseSetter closeSetter;
protected final ByteBufferPool bufferPool;
protected final HttpHandler rootHandler;
protected final OptionMap undertowOptions;
protected final StreamSourceConduit originalSourceConduit;
protected final StreamSinkConduit originalSinkConduit;
protected final List closeListeners = new LinkedList<>();
protected HttpServerExchange current;
private final int bufferSize;
private XnioBufferPoolAdaptor poolAdaptor;
/**
* Any extra bytes that were read from the channel. This could be data for this requests, or the next response.
*/
protected PooledByteBuffer extraBytes;
public AbstractServerConnection(StreamConnection channel, final ByteBufferPool bufferPool, final HttpHandler rootHandler, final OptionMap undertowOptions, final int bufferSize) {
this.channel = channel;
this.bufferPool = bufferPool;
this.rootHandler = rootHandler;
this.undertowOptions = undertowOptions;
this.bufferSize = bufferSize;
closeSetter = new CloseSetter();
if (channel != null) {
this.originalSinkConduit = channel.getSinkChannel().getConduit();
this.originalSourceConduit = channel.getSourceChannel().getConduit();
channel.setCloseListener(closeSetter);
} else {
this.originalSinkConduit = null;
this.originalSourceConduit = null;
}
}
@Override
public Pool getBufferPool() {
if(poolAdaptor == null) {
poolAdaptor = new XnioBufferPoolAdaptor(getByteBufferPool());
}
return poolAdaptor;
}
/**
* Get the root HTTP handler for this connection.
*
* @return the root HTTP handler for this connection
*/
public HttpHandler getRootHandler() {
return rootHandler;
}
/**
* Get the buffer pool for this connection.
*
* @return the buffer pool for this connection
*/
@Override
public ByteBufferPool getByteBufferPool() {
return bufferPool;
}
/**
* Get the underlying channel.
*
* @return the underlying channel
*/
public StreamConnection getChannel() {
return channel;
}
@Override
public ChannelListener.Setter getCloseSetter() {
return closeSetter;
}
@Override
public XnioWorker getWorker() {
return channel.getWorker();
}
@Override
public XnioIoThread getIoThread() {
if(channel == null) {
return null;
}
return channel.getIoThread();
}
@Override
public boolean isOpen() {
return channel.isOpen();
}
@Override
public boolean supportsOption(final Option> option) {
return channel.supportsOption(option);
}
@Override
public T getOption(final Option option) throws IOException {
return channel.getOption(option);
}
@Override
public T setOption(final Option option, final T value) throws IllegalArgumentException, IOException {
return channel.setOption(option, value);
}
@Override
public void close() throws IOException {
channel.close();
}
@Override
public SocketAddress getPeerAddress() {
return channel.getPeerAddress();
}
@Override
public A getPeerAddress(final Class type) {
return channel.getPeerAddress(type);
}
@Override
public SocketAddress getLocalAddress() {
return channel.getLocalAddress();
}
@Override
public A getLocalAddress(final Class type) {
return channel.getLocalAddress(type);
}
@Override
public OptionMap getUndertowOptions() {
return undertowOptions;
}
/**
* @return The size of the buffers allocated by the buffer pool
*/
@Override
public int getBufferSize() {
return bufferSize;
}
public PooledByteBuffer getExtraBytes() {
if(extraBytes != null && !extraBytes.getBuffer().hasRemaining()) {
extraBytes.close();
extraBytes = null;
return null;
}
return extraBytes;
}
public void setExtraBytes(final PooledByteBuffer extraBytes) {
this.extraBytes = extraBytes;
}
/**
* @return The original source conduit
*/
public StreamSourceConduit getOriginalSourceConduit() {
return originalSourceConduit;
}
/**
* @return The original underlying sink conduit
*/
public StreamSinkConduit getOriginalSinkConduit() {
return originalSinkConduit;
}
/**
* Resets the channel to its original state, effectively disabling all current conduit
* wrappers. The current state is encapsulated inside a {@link ConduitState} object that
* can be used the restore the channel.
*
* @return An opaque representation of the previous channel state
*/
public ConduitState resetChannel() {
ConduitState ret = new ConduitState(channel.getSinkChannel().getConduit(), channel.getSourceChannel().getConduit());
channel.getSinkChannel().setConduit(originalSinkConduit);
channel.getSourceChannel().setConduit(originalSourceConduit);
return ret;
}
/**
* Resets the channel to its original state, effectively disabling all current conduit
* wrappers. The current state is lost.
*/
public void clearChannel() {
channel.getSinkChannel().setConduit(originalSinkConduit);
channel.getSourceChannel().setConduit(originalSourceConduit);
}
/**
* Restores the channel conduits to a previous state.
*
* @param state The original state
* @see #resetChannel()
*/
public void restoreChannel(final ConduitState state) {
channel.getSinkChannel().setConduit(state.sink);
channel.getSourceChannel().setConduit(state.source);
}
public static class ConduitState {
final StreamSinkConduit sink;
final StreamSourceConduit source;
private ConduitState(final StreamSinkConduit sink, final StreamSourceConduit source) {
this.sink = sink;
this.source = source;
}
}
protected static StreamSinkConduit sink(ConduitState state) {
return state.sink;
}
protected static StreamSourceConduit source(ConduitState state) {
return state.source;
}
@Override
public void addCloseListener(CloseListener listener) {
this.closeListeners.add(listener);
}
@Override
protected ConduitStreamSinkChannel getSinkChannel() {
return channel.getSinkChannel();
}
@Override
protected ConduitStreamSourceChannel getSourceChannel() {
return channel.getSourceChannel();
}
protected void setUpgradeListener(HttpUpgradeListener upgradeListener) {
throw UndertowMessages.MESSAGES.upgradeNotSupported();
}
@Override
protected void maxEntitySizeUpdated(HttpServerExchange exchange) {
}
private class CloseSetter implements ChannelListener.Setter, ChannelListener {
private ChannelListener super ServerConnection> listener;
@Override
public void set(ChannelListener super ServerConnection> listener) {
this.listener = listener;
}
@Override
public void handleEvent(StreamConnection channel) {
try {
for (CloseListener l : closeListeners) {
try {
l.closed(AbstractServerConnection.this);
} catch (Throwable e) {
UndertowLogger.REQUEST_LOGGER.exceptionInvokingCloseListener(l, e);
}
}
if (current != null) {
current.endExchange();
}
ChannelListeners.invokeChannelListener(AbstractServerConnection.this, listener);
} finally {
if(extraBytes != null) {
extraBytes.close();
extraBytes = null;
}
}
}
}
}