/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is finishThread software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package com.caucho.network.listen; import java.util.logging.Level; import java.util.logging.Logger; import com.caucho.inject.Module; @Module enum SocketLinkState { /** * The allocated, ready to accept state */ INIT { @Override boolean isAllowIdle() { return true; } @Override SocketLinkState toInit(TcpSocketLink conn) { return INIT; } @Override SocketLinkState toIdle() { return IDLE; } @Override SocketLinkState toAccept() { return ACCEPT; } }, /** * Waiting in an accept() for a new connection */ ACCEPT { // accepting @Override boolean isActive() { return true; } @Override SocketLinkState toActiveWithKeepalive(TcpSocketLink conn) { conn.getListener().keepaliveAllocate(); return REQUEST_ACTIVE_KA; } @Override SocketLinkState toActiveNoKeepalive(TcpSocketLink conn) { return REQUEST_ACTIVE_NKA; } }, /** * Processing a request with a keepalive slot allocated. */ REQUEST_ACTIVE_KA { // processing a request @Override boolean isActive() { return true; } @Override boolean isRequestActive() { return true; } @Override boolean isKeepaliveAllocated() { return true; } @Override SocketLinkState toActiveWithKeepalive(TcpSocketLink conn) { return REQUEST_ACTIVE_KA; } @Override SocketLinkState toActiveNoKeepalive(TcpSocketLink conn) { conn.getListener().keepaliveFree(); return REQUEST_ACTIVE_NKA; } @Override SocketLinkState toKillKeepalive(TcpSocketLink conn) { conn.getListener().keepaliveFree(); return REQUEST_ACTIVE_NKA; } @Override SocketLinkState toKeepalive(TcpSocketLink conn) { return KEEPALIVE_THREAD; } @Override SocketLinkState toComet() { return COMET_KA; } @Override SocketLinkState toDuplex(TcpSocketLink conn) { conn.getListener().keepaliveFree(); return DUPLEX; } @Override SocketLinkState toClosed(TcpSocketLink conn) { conn.getListener().keepaliveFree(); return CLOSED; } }, /** * Request active with keepalive forbidden. */ REQUEST_ACTIVE_NKA { // processing a request, but keepalive forbidden @Override boolean isActive() { return true; } @Override SocketLinkState toComet() { return COMET_NKA; } @Override SocketLinkState toDuplex(TcpSocketLink conn) { return DUPLEX; } }, /** * Waiting for a read from the keepalive connection. */ KEEPALIVE_THREAD { // waiting for keepalive data @Override boolean isKeepalive() { return true; } @Override boolean isKeepaliveAllocated() { return true; } @Override SocketLinkState toActiveWithKeepalive(TcpSocketLink conn) { return REQUEST_ACTIVE_KA; } @Override SocketLinkState toActiveNoKeepalive(TcpSocketLink conn) { conn.getListener().keepaliveFree(); return REQUEST_ACTIVE_NKA; } @Override SocketLinkState toKeepaliveSelect() { return KEEPALIVE_SELECT; } @Override SocketLinkState toClosed(TcpSocketLink conn) { conn.getListener().keepaliveFree(); return CLOSED; } }, KEEPALIVE_SELECT { // waiting for keepalive data (select) @Override boolean isKeepalive() { return true; } @Override boolean isKeepaliveAllocated() { return true; } @Override SocketLinkState toActiveWithKeepalive(TcpSocketLink conn) { return REQUEST_ACTIVE_KA; } @Override SocketLinkState toActiveNoKeepalive(TcpSocketLink conn) { conn.getListener().keepaliveFree(); return REQUEST_ACTIVE_NKA; } @Override SocketLinkState toClosed(TcpSocketLink conn) { conn.getListener().keepaliveFree(); return CLOSED; } }, /** * Comet request with a keepalive allocated. */ COMET_KA { // processing an active comet service @Override boolean isComet() { return true; } @Override boolean isCometActive() { return true; } @Override boolean isAsyncStarted() { return true; } @Override boolean isKeepaliveAllocated() { return true; } @Override SocketLinkState toCometSuspend(TcpSocketLink conn) { conn.getListener().cometSuspend(conn); return COMET_SUSPEND_KA; } @Override SocketLinkState toCometComplete() { return COMET_COMPLETE_KA; } @Override SocketLinkState toKillKeepalive(TcpSocketLink conn) { conn.getListener().keepaliveFree(); return COMET_NKA; } @Override SocketLinkState toClosed(TcpSocketLink conn) { conn.getListener().keepaliveFree(); return CLOSED; } }, COMET_NKA { // processing an active comet service @Override boolean isComet() { return true; } @Override boolean isCometActive() { return true; } @Override boolean isAsyncStarted() { return true; } @Override SocketLinkState toCometSuspend(TcpSocketLink conn) { conn.getListener().cometSuspend(conn); return COMET_SUSPEND_NKA; } @Override SocketLinkState toCometComplete() { return COMET_COMPLETE_NKA; } }, COMET_SUSPEND_KA { // suspended waiting for a wake @Override boolean isComet() { return true; } @Override boolean isCometSuspend() { return true; } @Override boolean isAsyncStarted() { return true; } @Override boolean isKeepaliveAllocated() { return true; } @Override SocketLinkState toKillKeepalive(TcpSocketLink conn) { conn.getListener().keepaliveFree(); return COMET_SUSPEND_NKA; } @Override SocketLinkState toCometResume(TcpSocketLink conn) { conn.getListener().cometDetach(conn); return REQUEST_ACTIVE_KA; } @Override SocketLinkState toClosed(TcpSocketLink conn) { conn.getListener().cometDetach(conn); throw new IllegalStateException(this + " " + conn); } @Override SocketLinkState toDestroy(TcpSocketLink conn) { conn.getListener().cometDetach(conn); throw new IllegalStateException(this + " " + conn); } }, COMET_SUSPEND_NKA { // suspended waiting for a wake @Override boolean isComet() { return true; } @Override boolean isCometSuspend() { return true; } @Override boolean isAsyncStarted() { return true; } @Override SocketLinkState toCometResume(TcpSocketLink conn) { conn.getListener().cometDetach(conn); return REQUEST_ACTIVE_NKA; } @Override SocketLinkState toClosed(TcpSocketLink conn) { conn.getListener().cometDetach(conn); throw new IllegalStateException(this + " " + conn); } @Override SocketLinkState toDestroy(TcpSocketLink conn) { conn.getListener().cometDetach(conn); throw new IllegalStateException(this + " " + conn); } }, COMET_COMPLETE_KA { // complete or timeout @Override boolean isComet() { return true; } @Override boolean isCometComplete() { return true; } @Override boolean isKeepaliveAllocated() { return true; } @Override SocketLinkState toActiveWithKeepalive(TcpSocketLink conn) { return REQUEST_ACTIVE_KA; } @Override SocketLinkState toActiveNoKeepalive(TcpSocketLink conn) { conn.getListener().keepaliveFree(); return REQUEST_ACTIVE_NKA; } @Override SocketLinkState toKillKeepalive(TcpSocketLink conn) { conn.getListener().keepaliveFree(); return COMET_COMPLETE_NKA; } @Override SocketLinkState toCometComplete() { return this; } @Override SocketLinkState toKeepalive(TcpSocketLink conn) { return KEEPALIVE_THREAD; } @Override SocketLinkState toClosed(TcpSocketLink conn) { conn.getListener().keepaliveFree(); return CLOSED; } }, COMET_COMPLETE_NKA { // complete or timeout @Override boolean isComet() { return true; } @Override boolean isCometComplete() { return true; } @Override SocketLinkState toCometComplete() { return this; } }, DUPLEX { // converted to a duplex/websocket @Override boolean isDuplex() { return true; } @Override SocketLinkState toKeepalive(TcpSocketLink conn) { conn.getListener().duplexKeepaliveBegin(); return DUPLEX_KEEPALIVE; } @Override SocketLinkState toDuplexActive(TcpSocketLink conn) { return DUPLEX; } }, DUPLEX_KEEPALIVE { // waiting for duplex read data @Override boolean isDuplex() { return true; } @Override boolean isKeepalive() { return true; } @Override SocketLinkState toKeepaliveSelect() { return DUPLEX_KEEPALIVE; } @Override SocketLinkState toDuplexActive(TcpSocketLink conn) { conn.getListener().duplexKeepaliveEnd(); return DUPLEX; } @Override SocketLinkState toClosed(TcpSocketLink conn) { conn.getListener().duplexKeepaliveEnd(); return CLOSED; } }, CLOSED { // connection closed, ready for accept @Override boolean isClosed() { return true; } @Override boolean isAllowIdle() { return true; } @Override SocketLinkState toAccept() { return ACCEPT; } @Override SocketLinkState toIdle() { return IDLE; } }, IDLE { // TcpConnection in free list @Override boolean isIdle() { return true; } @Override SocketLinkState toInit(TcpSocketLink conn) { return INIT; } @Override SocketLinkState toAccept() { return ACCEPT; } @Override SocketLinkState toFree(TcpSocketLink conn) { return this; } @Override SocketLinkState toDestroy(TcpSocketLink conn) { throw new IllegalStateException(this + " is an illegal destroy state"); } }, DESTROYED { // connection destroyed @Override boolean isClosed() { return true; } @Override boolean isDestroyed() { return true; } @Override SocketLinkState toClosed(TcpSocketLink conn) { return this; } @Override SocketLinkState toDestroy(TcpSocketLink conn) { return this; } }; // fields private static final Logger log = Logger.getLogger(SocketLinkState.class.getName()); // // predicates // boolean isIdle() { return false; } boolean isComet() { return false; } boolean isCometActive() { return false; } boolean isCometSuspend() { return false; } boolean isCometWake() { return false; } boolean isAsyncStarted() { return false; } boolean isCometComplete() { return false; } boolean isDuplex() { return false; } /** * True if a keepalive has been allocated, i.e. if the connection * is allowed to keepalive to the next request. */ boolean isKeepaliveAllocated() { return false; } /** * True if the state is one of the keepalive states, either * a true keepalive-select or duplex. */ boolean isKeepalive() { return false; } boolean isActive() { return false; } boolean isRequestActive() { return false; } boolean isClosed() { return false; } boolean isDestroyed() { return false; } boolean isAllowIdle() { return false; } // // state changes // /** * Convert from the idle (pooled) or closed state to the initial state * before accepting a connection. */ SocketLinkState toInit(TcpSocketLink conn) { throw new IllegalStateException(this + " cannot switch to init for " + conn); } /** * Change to the accept state. */ SocketLinkState toAccept() { throw new IllegalStateException(this + " cannot switch to accept"); } /** * Changes to the active state. */ SocketLinkState toActive(TcpSocketLink conn, long connectionStartTime) { if (conn.getListener().isKeepaliveAllowed(connectionStartTime)) return toActiveWithKeepalive(conn); else { if (log.isLoggable(Level.FINE)) log.fine(conn + " keepalive disallowed"); return toActiveNoKeepalive(conn); } } /** * Changes to the active state with the keepalive allocated. */ SocketLinkState toActiveWithKeepalive(TcpSocketLink conn) { throw new IllegalStateException(this + " cannot switch to active"); } /** * Changes to the active state with no keepalive allocatedn. */ SocketLinkState toActiveNoKeepalive(TcpSocketLink conn) { throw new IllegalStateException(this + " cannot switch to active"); } /** * Kill the keepalive, i.e. remove the keepalive allocation. */ SocketLinkState toKillKeepalive(TcpSocketLink conn) { return this; } SocketLinkState toKeepalive(TcpSocketLink conn) { throw new IllegalStateException(this + " cannot switch to keepalive"); } SocketLinkState toKeepaliveSelect() { throw new IllegalStateException(this + " cannot switch to keepalive select"); } // // comet // SocketLinkState toComet() { throw new IllegalStateException(this + " cannot switch to comet"); } SocketLinkState toCometSuspend(TcpSocketLink conn) { throw new IllegalStateException(this + " cannot suspend comet"); } /* SocketLinkState toCometResume() { throw new IllegalStateException(this + " cannot resume comet"); } */ SocketLinkState toCometResume(TcpSocketLink conn) { throw new IllegalStateException(this + " cannot resume comet"); } SocketLinkState toCometDispatch() { throw new IllegalStateException(this + " cannot dispatch comet"); } SocketLinkState toCometComplete() { throw new IllegalStateException(this + " cannot complete comet"); } // // duplex/websocket // SocketLinkState toDuplex(TcpSocketLink conn) { throw new IllegalStateException(this + " cannot switch to duplex/websocket"); } SocketLinkState toDuplexActive(TcpSocketLink conn) { throw new IllegalStateException(this + " cannot switch to duplex/websocket"); } // // idle/close // SocketLinkState toIdle() { throw new IllegalStateException(this + " is an illegal idle state"); } SocketLinkState toFree(TcpSocketLink conn) { throw new IllegalStateException(this + " is an illegal free state for " + conn); } SocketLinkState toClosed(TcpSocketLink conn) { return CLOSED; } SocketLinkState toDestroy(TcpSocketLink conn) { toClosed(conn); conn.getListener().closeConnection(conn); return DESTROYED; } }