io.undertow.server.protocol.ParseTimeoutUpdater Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
/*
* 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.protocol;
import io.undertow.UndertowLogger;
import io.undertow.server.ServerConnection;
import io.undertow.util.WorkerUtils;
import org.xnio.IoUtils;
import org.xnio.XnioExecutor;
import org.xnio.channels.ConnectedChannel;
import java.io.Closeable;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
/**
* Wrapper for parse timeout.
*
* @author Sebastian Laskawiec
* @see io.undertow.UndertowOptions#REQUEST_PARSE_TIMEOUT
*/
public final class ParseTimeoutUpdater implements Runnable, ServerConnection.CloseListener, Closeable {
private final ConnectedChannel connection;
private final long requestParseTimeout;
private final long requestIdleTimeout;
private volatile XnioExecutor.Key handle;
private volatile long expireTime = -1;
private volatile boolean parsing = false;
//we add 50ms to the timeout to make sure the underlying channel has actually timed out
private static final int FUZZ_FACTOR = 50;
private final Runnable closeTask;
/**
* Creates new instance of ParseTimeoutSourceConduit.
* @param channel Channel which will be closed in case of timeout.
* @param requestParseTimeout Timeout value. Negative value will indicate that this updated is disabled.
* @param requestIdleTimeout
*/
public ParseTimeoutUpdater(ConnectedChannel channel, long requestParseTimeout, long requestIdleTimeout) {
this(channel, requestParseTimeout, requestIdleTimeout, new Runnable() {
@Override
public void run() {
IoUtils.safeClose(channel);
}
});
}
/**
* Creates new instance of ParseTimeoutSourceConduit.
* @param channel Channel which will be closed in case of timeout.
* @param requestParseTimeout Timeout value. Negative value will indicate that this updated is disabled.
* @param requestIdleTimeout
*/
public ParseTimeoutUpdater(ConnectedChannel channel, long requestParseTimeout, long requestIdleTimeout, Runnable closeTask) {
this.connection = channel;
this.requestParseTimeout = requestParseTimeout;
this.requestIdleTimeout = requestIdleTimeout;
this.closeTask = closeTask;
}
/**
* Called when the connection goes idle
*/
public void connectionIdle() {
parsing = false;
handleSchedule(requestIdleTimeout);
}
private void handleSchedule(long timeout) {
//no current timeout, clear the expire time
if(timeout == -1) {
this.expireTime = -1;
return;
}
//calculate the new expire time
long newExpireTime = System.currentTimeMillis() + timeout;
long oldExpireTime = this.expireTime;
this.expireTime = newExpireTime;
//if the new one is less than the current one we need to schedule a new timer, so cancel the old one
if(newExpireTime < oldExpireTime) {
if(handle != null) {
handle.remove();
handle = null;
}
}
if(handle == null) {
try {
handle = WorkerUtils.executeAfter(connection.getIoThread(), this, timeout + FUZZ_FACTOR, TimeUnit.MILLISECONDS);
} catch (RejectedExecutionException e) {
UndertowLogger.REQUEST_LOGGER.debug("Failed to schedule parse timeout, server is probably shutting down", e);
}
}
}
/**
* Called when a request is received, however it is not parsed in a single read() call. This starts a timer,
* and if the request is not parsed within this time then the connection is closed.
*
*/
public void failedParse() {
if(!parsing) {
parsing = true;
handleSchedule(requestParseTimeout);
}
}
/**
* Cancels timeout countdown.
*
* Should be called after parsing is complete (to avoid closing connection during other activities).
*
*/
public void requestStarted() {
expireTime = -1;
parsing = false;
}
@Override
public void run() {
if(!connection.isOpen()) {
return;
}
handle = null;
if (expireTime > 0) { //timeout is not active
long now = System.currentTimeMillis();
if(expireTime > now) {
handle = WorkerUtils.executeAfter(connection.getIoThread(), this, (expireTime - now) + FUZZ_FACTOR, TimeUnit.MILLISECONDS);
} else {
if(parsing) {
UndertowLogger.REQUEST_LOGGER.parseRequestTimedOut(connection.getPeerAddress());
} else {
UndertowLogger.REQUEST_LOGGER.debugf("Timing out idle connection from %s", connection.getPeerAddress());
}
closeTask.run();
}
}
}
@Override
public void closed(ServerConnection connection) {
close();
}
public void close() {
if(handle != null) {
handle.remove();
handle = null;
}
}
}