/*
 * 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 Also:
/** * 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.
Params:
  • requestParseTimeout – Timeout value. Negative value will indicate that this updated is disabled.
  • requestIdleTimeout –
/** * 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.
Params:
  • requestParseTimeout – Timeout value. Negative value will indicate that this updated is disabled.
  • requestIdleTimeout –
/** * 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
/** * 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.
/** * 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).

/** * Cancels timeout countdown. * <p> * Should be called after parsing is complete (to avoid closing connection during other activities). * </p> */
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; } } }