All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.caucho.v5.network.port.ConnectionTcp Maven / Gradle / Ivy

There is a newer version: 1.0.1
Show newest version
/*
 * Copyright (c) 1998-2015 Caucho Technology -- all rights reserved
 *
 * This file is part of Baratine(TM)
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Baratine is 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.
 *
 * Baratine 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 Baratine; 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.v5.network.port;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketTimeoutException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.caucho.v5.health.shutdown.Shutdown;
import com.caucho.v5.io.ClientDisconnectException;
import com.caucho.v5.io.ReadBuffer;
import com.caucho.v5.io.SocketBar;
import com.caucho.v5.io.StreamImpl;
import com.caucho.v5.io.WriteBuffer;
import com.caucho.v5.util.CurrentTime;
import com.caucho.v5.util.Friend;
import com.caucho.v5.util.ModulePrivate;

import io.baratine.service.ServiceRef;

/**
 * A protocol-independent TcpConnection.  TcpConnection controls the
 * TCP Socket and provides buffered streams.
 *
 * 

Each TcpConnection has its own thread. */ @ModulePrivate public class ConnectionTcp implements ConnectionTcpApi, ConnectionTcpProxy { private static final Logger log = Logger.getLogger(ConnectionTcp.class.getName()); private final long _connectionId; // The connection's id private final String _id; private final String _name; private String _dbgId; private final PortTcp _port; private final SocketBar _socket; private final ConnectionProtocol _protocol; private ReadBuffer _readStream; private WriteBuffer _writeStream; private final PollControllerTcp _pollHandle; private final ClassLoader _loader; private final ConnectionTcpProxy _connProxy; private StateConnection _state = StateConnection.FREE; private long _idleTimeout; private long _connectionSequence; private long _connectionStartTime; private long _idleStartTime; private long _idleExpireTime; private ServiceRef _inRef; /** * Creates a new TcpConnection. * * @param server The TCP server controlling the connections * @param request The protocol Request */ ConnectionTcp(long connId, PortTcp port, SocketBar socket) { _socket = socket; _writeStream = new WriteBuffer(); _writeStream.reuseBuffer(true); _readStream = new ReadBuffer(); _readStream.reuseBuffer(true); _connectionId = connId; _port = port; _loader = port.classLoader(); Protocol protocol = port.protocol(); _protocol = protocol.newConnection(this); // _id = listener.getDebugId() + "-" + _idCount; _id = protocol.name() + "-" + _port.port() + "-" + _connectionId; _inRef = port.ampManager().newService(this).name(_id).ref(); _connProxy = _inRef.as(ConnectionTcpProxy.class); _name = _id; //_connectionTask = new TaskConnection(this); _pollHandle = new PollControllerTcp(this); } /** * The connection's buffered read stream. If the ReadStream * needs to block, it will automatically flush the corresponding * WriteStream. */ @Override public final ReadBuffer readStream() { return _readStream; } /** * The connection's buffered write stream. If the ReadStream * needs to block, it will automatically flush the corresponding * WriteStream. */ @Override public final WriteBuffer writeStream() { return _writeStream; } /** * Returns the connection id. Primarily for debugging. */ @Override public long id() { return _connectionId; } public String getDebugId() { return _id; } /** * Returns the connection sequence. */ public long sequence() { return _connectionSequence; } /** * Returns the object name for jmx. */ public String getName() { return _name; } public String getThreadName() { return dbgId(); } /** * Returns the port which generated the connection. */ @Override public PortTcp port() { return _port; } /* private QueueService getTaskQueue() { return getPort().getTaskQueue(); } */ /* private IdleThreadManager getThreadManager() { return port().getThreadManager(); } */ /** * Returns the request for the connection. */ public final ConnectionProtocol protocol() { return _protocol; } /** * Returns the admin */ /* public TcpConnectionInfo getConnectionInfo() { TcpConnectionInfo connectionInfo = null; if (isActive()) { connectionInfo = new TcpConnectionInfo(getId(), getThreadId(), _port.getAddress(), _port.getPort(), getDisplayState(), getRequestStartTime()); if (connectionInfo.hasRequest()) { connectionInfo.setRemoteAddress(getRemoteHost()); connectionInfo.setUrl(getRequestUrl()); } } return connectionInfo; } */ public String getRequestUrl() { ConnectionProtocol request = protocol(); String url = request.url(); if (url != null && ! "".equals(url)) { return url; } PortTcp port = port(); String protocolName = port.protocolName(); if (protocolName == null) { protocolName = "request"; } if (port.address() == null) { return protocolName + "://*:" + port.port(); } else { return protocolName + "://" + port.address() + ":" + port.port(); } } // // timeout properties // /** * Sets the idle time for a keepalive connection. */ @Override public void setIdleTimeout(long idleTimeout) { _idleTimeout = idleTimeout; } /** * The idle time for a keepalive connection */ @Override public long getIdleTimeout() { return _idleTimeout; } // // port information // @Override public boolean isPortActive() { return _port.isActive(); } // // state information // /** * Returns the state. */ @Override public StateConnection getState() { return _state; } public String getStateName() { return String.valueOf(getState()); } public final boolean isIdle() { return _state.isIdle(); } /** * Returns true for active. */ public boolean isActive() { return _state.isActive(); } /** * Returns true for active. */ public boolean isRequestActive() { return isActive(); } /* @Override public boolean isKeepaliveAllocated() { return _state.isKeepaliveAllocated(); } */ /** * Returns true for closed. */ public boolean isClosed() { return _state.isClose(); } public final boolean isDestroyed() { return _state.isDestroy(); } /* @Override public boolean isCometActive() { AsyncControllerTcp async = _async; return (_state.isCometActive() && async != null && ! async.isCompleteRequested()); } */ /* public boolean isAsyncStarted() { return _stateRef.get().isAsyncStarted(); } */ /* public boolean isAsyncComplete() { AsyncControllerTcp async = _async; return async != null && async.isCompleteRequested(); } */ // // port/socket information // /** * Returns the connection's socket */ public SocketBar socket() { return _socket; } /** * Returns the local address of the socket. */ @Override public InetAddress getLocalAddress() { return _socket.addressLocal(); } @Override public InetSocketAddress ipLocal() { return _socket.ipLocal(); } /** * Returns the local host name. */ @Override public String getLocalHost() { return _socket.getLocalHost(); } /** * Returns the socket's local TCP port. */ @Override public int getLocalPort() { return _socket.portLocal(); } /** * Returns the socket's remote address. */ @Override public InetAddress getRemoteAddress() { return _socket.addressRemote(); } /** * Returns the socket's remote host name. */ @Override public String ip() { return _socket.getRemoteHost(); } /** * Adds from the socket's remote address. */ @Override public int getRemoteAddress(byte []buffer, int offset, int length) { return _socket.getRemoteAddress(buffer, offset, length); } /** * Returns the socket's remote port */ @Override public int getRemotePort() { return _socket.portRemote(); } /** * Returns true if the connection is secure, i.e. a SSL connection */ @Override public boolean isSecure() { return _socket.isSecure() || _port.isSecure(); } /** * Returns the virtual host. */ @Override public String getVirtualHost() { return port().getVirtualHost(); } // // SSL api // /** * Returns the cipher suite */ @Override public String secureProtocol() { return _socket.secureProtocol(); } /** * Returns the cipher suite */ @Override public String cipherSuite() { return _socket.cipherSuite(); } /*** * Returns the key size. */ /* @Override public int keySize() { return _socket.cipherBits(); } */ /** * Returns any client certificates. * @throws CertificateException */ @Override public X509Certificate []clientCertificates() throws CertificateException { return _socket.getClientCertificates(); } // // connection information // /** * Returns the time the connection started */ public final long getConnectionStartTime() { return _connectionStartTime; } // // request information // /** * Returns the idle expire time (keepalive or suspend). */ @Override public long idleExpireTime() { return _idleExpireTime; } /** * Returns the idle start time (keepalive or suspend) */ @Override public long idleStartTime() { return _idleStartTime; } // // statistics state // /** * Returns the user statistics state */ public String getDisplayState() { return _state.toString(); } /** * Sets the user statistics state */ /* private void setStatState(String state) { // _displayState = state; } */ @Override public ConnectionTcpProxy proxy() { return _connProxy; } // // async/comet predicates // /** * Poll the socket to test for an end-of-file for a comet socket. */ @Friend(PortTcp.class) boolean isReadEof() { SocketBar socket = _socket; if (socket == null) { return true; } try { StreamImpl s = socket.stream(); return s.isEof(); /* int len = s.getAvailable(); if (len > 0 || len == ReadStream.READ_TIMEOUT) return false; else return true; */ } catch (Exception e) { log.log(Level.FINE, e.toString(), e); return true; } } // // transition requests from external threads (thread-safe) // /** * Wake a connection from a select/poll keepalive. */ @Override public void requestPollRead() { /* if (log.isLoggable(Level.FINEST)) { log.finest("request-wake " + getName() + " (count=" + _port.getThreadCount() + ", idle=" + _port.getIdleThreadCount() + ")"); } */ try { requestLoop(); } catch (Exception e) { log.log(Level.WARNING, e.toString(), e); } /* if (_stateRef.get().toWake(_stateRef)) { offer(getConnectionTask()); } */ } /** * Wake a connection from a select/poll keepalive. */ @Override public void requestTimeout() { System.out.println("REQ_TO:" + this); requestCloseRead(); } /** * Wake a connection from a select/poll close. */ @Override public void requestCloseRead() { _state = _state.toCloseRead(); try { requestLoop(); } catch (Exception e) { log.log(Level.WARNING, e.toString(), e); } } /** * Wake a connection from a comet suspend. */ @Override public void requestWake() { try { _state = _state.toWake(); requestLoop(); } catch (Exception e) { log.log(Level.WARNING, e.toString(), e); } /* if (_stateRef.get().toWake(_stateRef)) { offer(getConnectionTask()); } */ } /** * Closes the controller. */ void requestCometComplete() { /* AsyncControllerTcp async = _async; if (async != null) { async.setCompleteRequested(); } */ try { requestWake(); } catch (Exception e) { log.log(Level.FINER, e.toString(), e); } } /** * Closes the controller. */ boolean requestCometTimeout() { /* AsyncControllerTcp async = _async; if (async != null) { async.setTimeout(); } */ System.out.println("REQ_COMT:"); /* try { if (_stateRef.get().toTimeoutWake(_stateRef)) { offer(getConnectionTask()); return true; } // requestWake(); } catch (Exception e) { log.log(Level.FINER, e.toString(), e); } */ return false; } /** * Closes the connection() */ /* public final void requestClose() { if (_requestStateRef.get().toClose(_requestStateRef)) { if (getLauncher().offerResumeTask(new CloseTask(this))) { return; } } requestDestroy(); } */ /** * Destroys the connection() */ @Override public final void requestDestroy() { destroy(); /* if (_stateRef.get().toDestroyWake(_stateRef)) { QueueService queue = getTaskQueue(); if (! getTaskQueue().offer(getConnectionTask())) { destroy(); } queue.wake(); } else { closeConnection(); } */ } /* private void offer(TaskConnection task) { try { QueueService queue = getTaskQueue(); if (! queue.offer(task, 120L, TimeUnit.SECONDS)) { log.severe(L.l("Schedule failed for {0}", this)); } queue.wake(); } catch (Exception e) { throw new RuntimeException(e); } } */ @Override public void requestShutdownBegin() { _port.requestShutdownBegin(); } @Override public void requestShutdownEnd() { _port.requestShutdownEnd(); } // // request handling // @Override public void requestAccept() { try { _state = _state.toAccepted(); initSocket(); _pollHandle.initKeepalive(); if (log.isLoggable(Level.FINE)) { _dbgId = (getLocalAddress().getHostAddress() + ":" + getLocalPort() + "<" + getRemoteAddress().getHostAddress() + ":" + getRemotePort() + "#" + _connectionId); } _protocol.onAccept(); requestLoop(); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } /** * Handles a new connection/socket from the client. */ private StateConnection requestLoop() throws IOException { StateConnection state = _state; StateConnection tailState = state; //Thread thread = Thread.currentThread(); //startThread(thread); try { while (! _port.isClosed()) { switch (state) { case IDLE: tailState = state; return tailState; case ACTIVE: ServiceRef.flushOutbox(); state = dispatchRequest(); break; case READ: ServiceRef.flushOutbox(); state = processPoll(); break; case POLL: ServiceRef.flushOutbox(); tailState = state; return tailState; case CLOSE_READ_S: ServiceRef.flushOutboxAndExecuteLast(); tailState = state; return tailState; /* case SUSPEND: //ServiceRef.flushOutbox(); //state = toSuspend(); tailState = NextState.SLEEP; return tailState; case SLEEP: tailState = state; return tailState; */ case CLOSE_READ_A: state = closeRead(); break; case CLOSE: _state = state; tailState = state; ServiceRef.flushOutboxAndExecuteLast(); close(); tailState = _state; return tailState; case FREE: tailState = state; ServiceRef.flushOutboxAndExecuteLast(); return tailState; /* case TIMEOUT: ServiceRef.flushOutbox(); //state = timeoutRequest(); return null; break; */ /* case DESTROY: ServiceRef.flushOutbox(); destroy(); tailState = state; return tailState; */ default: System.out.println("UNKNOWN-STATE: " + state + " " + this); _state = tailState = StateConnection.DESTROY; ServiceRef.flushOutbox(); destroy(); throw new IllegalStateException(String.valueOf(state)); } } } catch (ClientDisconnectException e) { // state = NextState.DESTROY; _port.addLifetimeClientDisconnectCount(); if (log.isLoggable(Level.FINER)) { log.finer(dbgId() + e); } } catch (InterruptedIOException e) { if (log.isLoggable(Level.FINEST)) { log.log(Level.FINEST, dbgId() + e, e); } } catch (IOException e) { if (log.isLoggable(Level.FINE)) { log.log(Level.FINE, dbgId() + e, e); } } catch (OutOfMemoryError e) { String msg = "TcpSocketLink OutOfMemory"; Shutdown.shutdownOutOfMemory(msg); } catch (Throwable e) { if (log.isLoggable(Level.FINE)) { log.log(Level.FINE, dbgId() + e, e); } } finally { _state = tailState; //thread.setContextClassLoader(_loader); //finishThread(tailState); //ServiceRef.flushOutboxAndExecuteLast(); } return state; } private StateConnection dispatchRequest() throws IOException { Thread thread = Thread.currentThread(); try { thread.setContextClassLoader(_loader); long requestTimeout = port().getRequestTimeout(); long now = CurrentTime.currentTime(); if (requestTimeout > 0) { long expireTime = now + requestTimeout; _socket.setRequestExpireTime(expireTime); _idleExpireTime = expireTime; } else { _idleExpireTime = now + 600 * 1000L; } StateConnection stateNext = protocol().service(); _state = _state.onServiceNext(stateNext); return _state; } finally { thread.setContextClassLoader(_loader); _socket.setRequestExpireTime(0); } } private StateConnection closeRead() throws IOException { // Thread thread = Thread.currentThread(); try { StateConnection stateNext = protocol().onCloseRead(); _state = _state.onServiceNext(stateNext); return _state; } finally { _socket.setRequestExpireTime(0); } } // // keepalives: read blocking and polling // /** * Starts a keepalive, either returning available data or * returning false to close the loop * * If keepaliveRead() returns true, data is available. * If it returns false, either the connection is closed, * or the connection has been registered with the select. */ private StateConnection processPoll() throws IOException { PortTcp port = _port; if (port.isClosed()) { return StateConnection.DESTROY; } if (readStream().available() > 0) { return StateConnection.ACTIVE; } long timeout = _idleTimeout; _idleStartTime = CurrentTime.currentTime(); _idleExpireTime = _idleStartTime + timeout; // _state = _state.toKeepalive(this); PollTcpManagerBase pollManager = port.getPollManager(); // use poll manager if available if (pollManager == null) { port().addLifetimeKeepaliveCount(); return threadPoll(timeout); } if (! _pollHandle.isKeepaliveStarted()) { ServiceRef.flushOutbox(); if (_port.keepaliveThreadRead(readStream(), _idleTimeout) > 0) { return StateConnection.ACTIVE; } else if (_idleExpireTime <= CurrentTime.currentTime()) { return StateConnection.TIMEOUT; } } /* if (! _state.toPollRequested(_stateRef)) { return _stateRef.get().getNextState(); } // _state = _state.toKeepaliveSelect(); * */ // keepalive to select manager succeeds switch (pollManager.startPoll(_pollHandle)) { case START: { if (log.isLoggable(Level.FINEST)) { log.finest(dbgId() + "keepalive (poll)"); } port().addLifetimeKeepaliveCount(); port().addLifetimeKeepalivePollCount(); return StateConnection.POLL; } case DATA: { if (log.isLoggable(Level.FINEST)) { log.finest("keepalive data available (poll) [" + dbgId() + "]"); } _state = _state.toWake(); /* if (_stateRef.get().toPollSleep(_stateRef)) { throw new IllegalStateException(); } */ return _state; } case CLOSED: { if (log.isLoggable(Level.FINEST)) { log.finest(dbgId() + " keepalive close (poll)"); } _state = _state.toWake(); /* if (_stateRef.get().toPollSleep(_stateRef)) { throw new IllegalStateException(); } */ return StateConnection.CLOSE; } default: throw new IllegalStateException(); } } private StateConnection threadPoll(long timeout) { // long timeout = getPort().getKeepaliveTimeout(); long expires = timeout + CurrentTime.getCurrentTimeActual(); if (log.isLoggable(Level.FINEST)) { log.finest(dbgId() + " keepalive (thread, " + timeout + "ms)"); } ServiceRef.flushOutbox(); do { try { long delta = expires - CurrentTime.getCurrentTimeActual(); if (delta < 0) { delta = 0; } long result = readStream().fillWithTimeout(delta); if (result > 0) { return StateConnection.ACTIVE; } else if (result < 0) { return StateConnection.CLOSE_READ_A; } } catch (SocketTimeoutException e) { log.log(Level.FINEST, e.toString(), e); } catch (IOException e) { log.log(Level.FINEST, e.toString(), e); break; } } while (CurrentTime.getCurrentTimeActual() < expires); // close(); // killKeepalive("thread-keepalive timeout (" + timeout + "ms)"); return StateConnection.CLOSE; } // // Callbacks from the request processing tasks // @Override public int fillWithTimeout(long timeout) throws IOException { return readStream().fillWithTimeout(timeout); } // // state transitions // /** * Initialize the socket for a new connection */ private void initSocket() throws IOException { _idleTimeout = _port.getKeepaliveTimeout(); _port.ssl(_socket); writeStream().init(_socket.stream()); // ReadStream cannot use getWriteStream or auto-flush // because of duplex mode // ReadStream is = getReadStream(); _readStream.init(_socket.stream()); if (log.isLoggable(Level.FINEST)) { log.finest(dbgId() + "starting connection " + this + ", total=" + _port.getConnectionCount()); } } @Override public void clientDisconnect() { // killKeepalive("client disconnect"); /* AsyncControllerTcp async = _async; if (async != null) { async.complete(); } */ } /* private StateConnection toSuspend() { _idleStartTime = CurrentTime.getCurrentTime(); _idleExpireTime = _idleStartTime + _suspendTimeout; AtomicReference stateRef = _stateRef; if (stateRef.get().toSuspendSleep(stateRef)) { return StateConnection.SLEEP; } else { return stateRef.get().getNextState(); } } */ /* private TaskConnection getConnectionTask() { return _connectionTask; } */ // // close operations // /* private void close() { setStatState(null); closeConnection(); } */ /** * Closes the connection. */ private void closeConnection() { //StateConnection state = _state; //_state = state.toClosed(this); // if (state.isKeepalive() && _port.getPollManager() != null) { // XXX: ka state //_keepalive.destroy(); // _port.getPollManager().closeKeepalive(_keepalive); disconnect(); if (log.isLoggable(Level.FINE)) { _dbgId = (port().address() + ":" + port().port() + "-" + _connectionId); Thread.currentThread().setName(_dbgId); } } @Override public void disconnect() { if (_state.isClose()) { return; } _state = _state.toClose(); // synchronized because shutdown can call disconnect on multiple threads synchronized (this) { _protocol.onCloseRead(); // XXX: ka state _pollHandle.destroy(); try { writeStream().close(); } catch (Throwable e) { log.log(Level.FINER, e.toString(), e); } try { ReadBuffer readStream = readStream(); if (readStream != null) { readStream.close(); } } catch (Throwable e) { log.log(Level.FINER, e.toString(), e); } PortTcp port = port(); SocketBar socket = _socket; if (port != null) { port.closeSocket(socket); } try { socket.close(); } catch (Throwable e) { log.log(Level.FINER, e.toString(), e); } try { protocol().onClose(); } catch (Throwable e) { log.warning(e.toString()); if (log.isLoggable(Level.FINER)) { log.log(Level.FINER, e.toString(), e); } } if (log.isLoggable(Level.FINER)) { if (port != null) log.finer("closing connection " + dbgId() + ", total=" + port.getConnectionCount()); else log.finer("closing connection " + id()); } } } /** * Called after close_read */ //@Override private void close() { if (! _state.isClose()) { System.out.println("Expected close at: " + _state);; Thread.dumpStack(); throw new IllegalStateException(_state.toString()); } //_state = _state.toFree(); // synchronized because shutdown can call disconnect on multiple threads synchronized (this) { //_protocol.onCloseRead(); // XXX: ka state _pollHandle.destroy(); try { writeStream().close(); } catch (Throwable e) { log.log(Level.FINER, e.toString(), e); } try { readStream().close(); } catch (Throwable e) { log.log(Level.FINER, e.toString(), e); } PortTcp port = port(); SocketBar socket = _socket; if (port != null) { port.closeSocket(socket); } try { socket.close(); } catch (Throwable e) { log.log(Level.FINER, e.toString(), e); } try { protocol().onClose(); } catch (Throwable e) { log.warning(e.toString()); if (log.isLoggable(Level.FINER)) { log.log(Level.FINER, e.toString(), e); } } // XXX: recycle if (log.isLoggable(Level.FINER)) { if (port != null) log.finer("closing connection " + dbgId() + ", total=" + port.getConnectionCount()); else log.finer("closing connection " + id()); } _state = _state.toFree(); if (_state.isFree()) { _port.freeConnection(this); } } } private void destroy() { if (log.isLoggable(Level.FINEST)) { log.finest(this + " destroying connection"); } try { _socket.forceShutdown(); } catch (Throwable e) { } try { closeConnection(); } catch (Throwable e) { log.log(Level.FINER, e.toString(), e); } // XXX: _port.removeConnection(this); } private String dbgId() { if (_dbgId == null) { /* Object serverId = Environment.getAttribute("caucho.server-id"); if (serverId != null) _dbgId = (getClass().getSimpleName() + "[id=" + getId() + ",seq=" + _connectionSequence + "," + serverId + "] "); else _dbgId = (getClass().getSimpleName() + "[id=" + getId() + "] "); */ if (_port != null) { _dbgId = _port.getUrl() + '-' + id(); } else { _dbgId = getClass().getSimpleName() + '-' + id(); } } return _dbgId; } @Override public int hashCode() { return (int) _connectionId; } public boolean equals(Object obj) { return this == obj; } @Override public String toString() { return (getClass().getSimpleName() + "[id=" + dbgId() + "," + _port.toURL() + ",seq=" + _connectionSequence + "," + _state + "]"); } enum Task { ACCEPT, KEEPALIVE; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy