All Implemented Interfaces:
Runnable, EventDispatcher
Direct Known Subclasses:
I2Ping, I2PSOCKSTunnel, I2PTunnelClient, I2PTunnelDCCClient, I2PTunnelHTTPClientBase, I2PTunnelIRCClient

public abstract class I2PTunnelClientBaseextends I2PTunnelTask implements Runnable
Abstract base class for I2P client tunnels with common functionality.

Manages I2PSocketManager creation, local server socket setup, connection threading, and framework for shared and non-shared client tunnels. Handles SSL configuration, connection timeouts, and provides abstract methods for connection-specific implementations.

Extended by specific tunnel types like HTTP clients, SOCKS proxies, and standard TCP tunnels.

  • Field Details

    • _log

      protected final Log _log
      The logger.
    • _context

      protected final I2PAppContext _context
      The application context.
    • l

      protected final Logging l
      The logging instance.
    • DEFAULT_CONNECT_TIMEOUT

      static final long DEFAULT_CONNECT_TIMEOUT
      Default connect timeout.
      See Also:
    • MAX_TIMEOUT_FAILOVER

      static final int MAX_TIMEOUT_FAILOVER
      Legacy cap on consecutive connect timeouts before abandoning tunnel failover. The leg walk no longer stops on a timeout count — every configured leg is tried while the pool has capacity — so the walk is now bounded by REQUEST_CONNECT_DEADLINE_MS instead. Retained as a documented legacy bound.
      See Also:
    • REQUEST_CONNECT_DEADLINE_MS

      static final long REQUEST_CONNECT_DEADLINE_MS
      Single wall-clock budget for establishing one proxied request. Naming lookups, every tunnel-failover leg, retry sleeps, and outer connect re-walks all draw from one deadline computed from this budget instead of each keeping an independent limit, so a quantity-4 leg walk plus outer retries can no longer stack well past two minutes before the request gives up.
      See Also:
    • NO_DEADLINE

      static final long NO_DEADLINE
      Deadline sentinel for callers that have not adopted the shared request budget (SOCKS, IRC, DCC, raw createI2PSocket): they keep their original per-call limits because this sentinel never reads as expired and never clamps.
      See Also:
    • MIN_CONNECT_LEG_MS

      static final long MIN_CONNECT_LEG_MS
      Minimum remaining budget for starting one connect leg. Streaming floors connect timeouts at 10s (CONNECT_TIMEOUT_FLOOR_MS), so a leg with less than this left is guaranteed to be cut short — it can only burn the shared deadline on a doomed attempt. The walk skips such a leg instead.
      See Also:
    • POOL_BUILD_DEADLINE_GRACE_MS

      static final long POOL_BUILD_DEADLINE_GRACE_MS
      One-shot deadline extension granted while the outbound tunnel pool is still building (poolState() == 0) when the shared budget runs out. No connect leg can succeed before the pool's first tunnel lands; a healthy first build completes inside this window, so expiring the request at the 120s mark would discard a request whose pool was seconds from ready. At most one extension per request, keeping the worst case at REQUEST_CONNECT_DEADLINE_MS + this value.
      See Also:
    • __clientId

      private static final AtomicLong __clientId
      Client ID counter.
    • _clientId

      protected long _clientId
      This client's ID.
    • sockLock

      protected final Object sockLock
      Guards sockMgr and mySockets.
    • sockMgr

      protected I2PSocketManager sockMgr
      The socket manager.
    • mySockets

      protected final List<I2PSocket> mySockets
      List of active sockets.
    • _ownDest

      protected boolean _ownDest
      Whether we own our destination.
    • dest

      protected Destination dest
      The destination.
    • localPort

      private volatile int localPort
      Local port.
    • _handlerName

      private final String _handlerName
      Handler name.
    • listenerReady

      protected boolean listenerReady
      Protected for I2Ping since 0.9.11. Not for use outside package.
    • ss

      protected ServerSocket ss
      Server socket.
    • startLock

      private final Object startLock
      Start lock.
    • startRunning

      private boolean startRunning
      Whether start is running.
    • _buildingTunnels

      private volatile boolean _buildingTunnels
      Whether we are building tunnels.
    • _tunnelBuilder

      private volatile Thread _tunnelBuilder
      Tunnel builder thread.
    • privKeyFile

      private String privKeyFile
      Private key file path.
    • chained

      private boolean chained
      True if we are chained from a server.
    • _executor

      protected volatile ThreadPoolExecutor _executor
      Thread pool executor.
    • _ownExecutor

      private volatile boolean _ownExecutor
      True if we created _executor ourselves (TCG was null) and must shut it down on close.
    • PROP_MAX_CONNECTIONS

      public static final String PROP_MAX_CONNECTIONS
      Property name: max concurrently active client connections handled by one client tunnel.
      See Also:
    • DEFAULT_SOCKET_OPEN_TIMEOUT

      public static final long DEFAULT_SOCKET_OPEN_TIMEOUT
      Default socket open timeout in ms, after which close(boolean) will proceed even with active sockets.
      See Also:
    • DEFAULT_MAX_CONNECTIONS

      public static final int DEFAULT_MAX_CONNECTIONS
      Default cap on concurrently handled client connections.

      The accept/connect path runs on an unbounded I2PTunnelClientBase.BlockingRunner pool, so a flood of inbound peer connections (e.g. tracker announces/scrapes) can spawn unlimited parallel connect attempts - each with its own retry loop - starving legitimate browsing streams. A hard concurrent-process cap sheds excess inbound load instead of amplifying it.

      See Also:
    • _maxConnections

      private volatile int _maxConnections
      Resolved concurrent connection cap for this tunnel. Guards #manageConnection.
    • _maxConnectionsCustomized

      private volatile boolean _maxConnectionsCustomized
      True when this tunnel's config explicitly set "i2ptunnel.maxConnections". When false the tunnel inherits the Tuner-managed default cap (see getEffectiveMaxConnections()), so the Tuner can raise/lower the floor without a tunnel restart. An explicit override always wins over the Tuner default.
    • _activeConnections

      private final AtomicInteger _activeConnections
      Live reservation counter shared by the connections of this tunnel.
    • _socketOpenTimeout

      private final long _socketOpenTimeout
      Socket open timeout in ms; close() will proceed after this even with active sockets.
      See Also:
    • _openStarted

      private long _openStarted
      Time the tunnel was opened, for timeout checks in close().
    • socketManager

      private static I2PSocketManager socketManager
      This is ONLY for shared clients.
    • socketManagerState

      private static I2PTunnelClientBase.SocketManagerState socketManagerState
      Whether this router's shared socket manager has connected yet.
    • PROP_USE_SSL

      public static final String PROP_USE_SSL
      Property turning on SSL between the proxy and the client.
      See Also:
    • RETRY_DELAY

      private static final int RETRY_DELAY
      Pause before retrying a failed tunnel: 15 seconds.
      See Also:
    • MAX_RETRIES

      private static final int MAX_RETRIES
      Most tunnel rebuild attempts before giving up.
      See Also:
  • Constructor Details

    • I2PTunnelClientBase

      public I2PTunnelClientBase(int localPort, Logging l, I2PSocketManager sktMgr, I2PTunnel tunnel, EventDispatcher notifyThis, long clientId) throws IllegalArgumentException
      This constructor is used to add a client to an existing socket manager.

      As of 0.9.21 this does NOT open the local socket. You MUST call startRunning for that. The local socket will be opened immediately (ignoring the i2cp.delayOpen option).

      Parameters:
      localPort - if 0, use any port, get actual port selected with getLocalPort()
      l - the logging instance
      sktMgr - the existing socket manager
      tunnel - the I2PTunnel instance
      notifyThis - the event dispatcher for notifications
      clientId - the client identifier
      Throws:
      IllegalArgumentException
    • I2PTunnelClientBase

      public I2PTunnelClientBase(int localPort, boolean ownDest, Logging l, EventDispatcher notifyThis, String handlerName, I2PTunnel tunnel) throws IllegalArgumentException
      The main constructor.

      As of 0.9.21 this is fast, and does NOT connect the manager to the router, or open the local socket. You MUST call startRunning() for that.

      (0.9.20 claimed to be fast, but due to a bug it DID connect the manager to the router. It did NOT open the local socket however, so it was still necessary to call startRunning() for that.).

      Parameters:
      localPort - if 0, use any port, get actual port selected with getLocalPort()
      ownDest - whether to use an owned destination
      l - the logging instance
      notifyThis - the event dispatcher for notifications
      handlerName - the handler name
      tunnel - the I2PTunnel instance
      Throws:
      IllegalArgumentException - if the I2CP configuration is b0rked so badly that we can't create a socketManager
    • I2PTunnelClientBase

      public I2PTunnelClientBase(int localPort, boolean ownDest, Logging l, EventDispatcher notifyThis, String handlerName, I2PTunnel tunnel, String pkf) throws IllegalArgumentException
      Use this to build a client with a persistent private key.

      As of 0.9.21 this is fast, and does NOT connect the manager to the router, or open the local socket. You MUST call startRunning() for that.

      (0.9.20 claimed to be fast, but due to a bug it DID connect the manager to the router. It did NOT open the local socket however, so it was still necessary to call startRunning() for that.).

      Parameters:
      localPort - if 0, use any port, get actual port selected with getLocalPort()
      ownDest - whether to use an owned destination
      l - the logging instance
      notifyThis - the event dispatcher for notifications
      handlerName - the handler name
      tunnel - the I2PTunnel instance
      pkf - Path to the private key file, or null to generate a transient key
      Throws:
      IllegalArgumentException - if the I2CP configuration is b0rked so badly that we can't create a socketManager
  • Method Details

    • verifySocketManager

      protected void verifySocketManager()
      Create the manager if it doesn't exist, AND connect it to the router and build tunnels.

      Sets the this.sockMgr field if it is null, or if we want a new one. This may take a LONG time if building a new manager.

      We need a socket manager before getDefaultOptions() and most other things.

      Throws:
      IllegalStateException - if the I2CP configuration is b0rked so badly that we cant create a socketManager
    • getSocketManager

      public I2PSocketManager getSocketManager()
      Returns the I2PSocketManager for this task. For non-shared clients returns the instance socket manager. For shared clients returns the shared static socket manager.
      Overrides:
      getSocketManager in class I2PTunnelTask
      Returns:
      the socket manager
    • getSocketManager

      protected static I2PSocketManager getSocketManager(I2PTunnel tunnel, String pkf)
      This is ONLY for shared clients. As of 0.9.20 this is fast, and does NOT connect the manager to the router. Call verifySocketManager() for that.
      Parameters:
      tunnel - the I2PTunnel instance
      pkf - the private key file path, or null
      Returns:
      non-null
      Throws:
      IllegalArgumentException - if the I2CP configuration is b0rked so badly that we cant create a socketManager
    • buildSocketManager

      protected I2PSocketManager buildSocketManager()
      For NON-SHARED clients (ownDest = true).

      As of 0.9.20 this is fast, and does NOT connect the manager to the router. Call verifySocketManager() for that.

      Returns:
      non-null
      Throws:
      IllegalArgumentException - if the I2CP configuration is b0rked so badly that we cant create a socketManager
    • buildSocketManager

      protected static I2PSocketManager buildSocketManager(I2PTunnel tunnel)
      Create a disconnected socket manager for the tunnel, with no private key file.

      As of 0.9.20 this is fast, and does NOT connect the manager to the router. Call verifySocketManager() for that.

      Parameters:
      tunnel - the I2PTunnel instance
      Returns:
      non-null
      Throws:
      IllegalArgumentException - if the I2CP configuration is b0rked so badly that we cant create a socketManager
    • buildSocketManager

      protected static I2PSocketManager buildSocketManager(I2PTunnel tunnel, String pkf)
      Create a disconnected socket manager for the tunnel, loading the persistent client dest from pkf if it is non-null.

      As of 0.9.20 this is fast, and does NOT connect the manager to the router. Call verifySocketManager() for that.

      Parameters:
      tunnel - the I2PTunnel instance
      pkf - absolute path or null
      Returns:
      non-null
      Throws:
      IllegalArgumentException - if the I2CP configuration is b0rked so badly that we cant create a socketManager
    • buildSocketManager

      protected static I2PSocketManager buildSocketManager(I2PTunnel tunnel, String pkf, Logging log)
      Create a disconnected socket manager for the tunnel, loading the persistent client dest from pkf if it is non-null, and logging any key file error to log.

      As of 0.9.20 this is fast, and does NOT connect the manager to the router. Call verifySocketManager() for that.

      Parameters:
      tunnel - the I2PTunnel instance
      pkf - absolute path or null
      log - the logging instance
      Returns:
      non-null
      Throws:
      IllegalArgumentException - if the I2CP configuration is b0rked so badly that we cant create a socketManager
    • connectManager

      private void connectManager()
      Warning, blocks while connecting to router and building tunnels; This may take a LONG time.
      Throws:
      IllegalArgumentException - if the I2CP configuration is b0rked so badly that we cant create a socketManager
    • getLocalPort

      public final int getLocalPort()
      Get the local port.
      Returns:
      the local port number
    • getListenHost

      protected final InetAddress getListenHost(Logging l)
      Get the listen host address.
      Parameters:
      l - the logging instance
      Returns:
      the InetAddress for the listen host, or null on error
    • startRunning

      public void startRunning()
      Actually open the local socket and start working on incoming connections. *Must* be called by derived classes after initialization.

      This will be fast if i2cp.delayOpen is true, but could take a LONG TIME if it is false, as it connects to the router and builds tunnels.

      Extending classes must check the value of boolean open after calling super.startRunning(), if false then something went wrong.

    • startup

      private void startup()
      Opens the tunnel and starts the socket manager.
    • getDefaultOptions

      protected I2PSocketOptions getDefaultOptions()
      Create the default options (using the default timeout, etc). Warning, this does not make a copy of I2PTunnel's client options, it modifies them directly.
      Returns:
      the default socket options
    • getDefaultOptions

      protected I2PSocketOptions getDefaultOptions(Properties overrides)
      Create the default options (using the default timeout, etc). Warning, this does not make a copy of I2PTunnel's client options, it modifies them directly. Do not use overrides for per-socket options.
      Parameters:
      overrides - the properties to override
      Returns:
      the default socket options with overrides
    • optionsUpdated

      public void optionsUpdated(I2PTunnel tunnel)
      Update the I2PSocketManager.
      Overrides:
      optionsUpdated in class I2PTunnelTask
      Parameters:
      tunnel - the tunnel whose client options were updated
    • createI2PSocket

      Create a new I2PSocket towards to the specified destination, adding it to the list of connections actually managed by this tunnel.
      Parameters:
      dest - The destination to connect to, non-null
      Returns:
      a new I2PSocket
      Throws:
      I2PException - if there is an I2P-related problem
      ConnectException - if the peer refuses the connection
      NoRouteToHostException - if the peer is not found or not reachable
      InterruptedIOException - if the connection times out
    • createI2PSocket

      Create a new I2PSocket towards to the specified destination, adding it to the list of connections actually managed by this tunnel.
      Parameters:
      dest - The destination to connect to, non-null
      port - The destination port to connect to 0 - 65535
      Returns:
      a new I2PSocket
      Throws:
      I2PException - if there is an I2P-related problem
      ConnectException - if the peer refuses the connection
      NoRouteToHostException - if the peer is not found or not reachable
      InterruptedIOException - if the connection times out
    • createI2PSocket

      Create a new I2PSocket towards to the specified destination, adding it to the list of connections actually managed by this tunnel.

      With multiple inbound or outbound tunnels configured (inbound.quantity > 1 or outbound.quantity > 1), the session's tunnel pool has multiple tunnels. If a connect attempt fails with NoRouteToHostException, the method retries, allowing the session's tunnel pool to select a different tunnel. This provides robust delivery and automatic tunnel failover.

      Parameters:
      dest - The destination to connect to, non-null
      opt - Option to be used to open when opening the socket
      Returns:
      a new I2PSocket
      Throws:
      ConnectException - if the peer refuses the connection
      NoRouteToHostException - if the peer is not found or not reachable
      InterruptedIOException - if the connection times out
      I2PException - if there is some other I2P-related problem
    • createI2PSocket

      Create a new I2PSocket bounded by a shared request deadline.

      The HTTP proxy threads one request-scoped deadline through naming, the failover walk, retry sleeps, and outer retries (REQUEST_CONNECT_DEADLINE_MS). The walk starts no leg once the deadline has passed and shortens each leg's connect timeout to the remaining budget, so the whole operation cannot exceed the deadline by a full connect timeout. Because a deadline may shorten opt in place, a caller passing one must not share the options object with other requests.

      Parameters:
      dest - The destination to connect to, non-null
      opt - Option to be used to open the socket
      deadlineMs - absolute wall-clock deadline in ms since the epoch, or NO_DEADLINE for no shared budget
      Returns:
      a new I2PSocket
      Throws:
      I2PException - if there is some other I2P-related problem
      ConnectException - if the peer refuses the connection
      NoRouteToHostException - if the peer is not found or not reachable after retries
      InterruptedIOException - if the connection times out
    • createI2PSocketWithFailover

      private I2PSocket createI2PSocketWithFailover(Destination dest, I2PSocketOptions opt, long deadlineMs) throws I2PException, ConnectException, NoRouteToHostException, InterruptedIOException
      Create a new I2PSocket with retry/failover across the session's tunnel pool.

      With multiple inbound or outbound tunnels configured, the session's tunnel pools have multiple tunnels available. If a connection attempt fails with NoRouteToHostException, the method retries, allowing the session's tunnel pool to select a different tunnel. The walk is additionally bounded by deadlineMs: no new leg is started after the deadline, each leg's connect timeout is clamped to the remaining budget (a default is substituted when the caller requested none, and legs with less than MIN_CONNECT_LEG_MS left are skipped rather than doomed), and one pool-state snapshot is taken per failure to decide whether another leg is worthwhile.

      Parameters:
      dest - The destination to connect to, non-null
      opt - the socket options, passed to the connection manager for the attempt
      deadlineMs - absolute wall-clock deadline in ms since the epoch, or NO_DEADLINE for no shared budget
      Returns:
      a new I2PSocket
      Throws:
      I2PException - if there is some other I2P-related problem
      ConnectException - if the peer refuses the connection
      NoRouteToHostException - if the peer is not found or not reachable after retries
      InterruptedIOException - if the connection times out
    • shouldContinueFailover

      static boolean shouldContinueFailover(int tunnelCount, int attemptsMade, int timeoutFailures, boolean poolDown)
      Whether another tunnel-failover attempt is worthwhile after a connect failure.

      Stops immediately when the outbound pool is provably dead (no valid tunnels, none building). Further legs cannot succeed and each leg may burn the full connect timeout. When a timeout occurred, walks all configured legs (up to tunnelCount) so a pool with several dead tunnels does not give up after MAX_TIMEOUT_FAILOVER legs while healthy legs remain untried. Non-timeout failures (NoRoute, refused) also walk all legs.

      Parameters:
      tunnelCount - configured max failover legs (max inbound/outbound quantity)
      attemptsMade - legs already attempted (1-based)
      timeoutFailures - consecutive timeout-style failures so far
      poolDown - true if poolIsDefinitivelyDown() was true after the failure
      Returns:
      true to try the next tunnel leg
    • shouldContinueFailover

      static boolean shouldContinueFailover(int tunnelCount, int attemptsMade, int timeoutFailures, boolean poolDown, boolean poolBuilding)
      Whether another tunnel-failover attempt is worthwhile after a connect failure.

      Stops immediately when the outbound pool is provably dead. Otherwise walks all configured legs (attemptsMade < tunnelCount) regardless of how many were timeouts — giving up after MAX_TIMEOUT_FAILOVER while healthy legs remain wastes the failover budget and surfaces "giving up" at tunnel 1/4. poolBuilding no longer extends the timeout cap; it is retained for call-site compatibility and future tuning.

      Parameters:
      tunnelCount - configured max failover legs (max inbound/outbound quantity)
      attemptsMade - legs already attempted (1-based)
      timeoutFailures - consecutive timeout-style failures so far
      poolDown - true if poolIsDefinitivelyDown() was true after the failure
      poolBuilding - true when poolState() reports builds in flight
      Returns:
      true to try the next tunnel leg
    • shouldOuterRetryConnect

      static boolean shouldOuterRetryConnect(int connectAttempts, int timeoutConnectAttempts, boolean timedOut, boolean poolDown, boolean poolBuilding, boolean browserClosed)
      Whether the outer HTTP connect loop should retry after a failure. createI2PSocketWithFailover already walked every tunnel leg; this decides whether to re-enter that walk.

      Policy: always allow at least one timeout retry so a pool that was mid-build when the first walk started can finish its replacements. While poolBuilding, allow a second timeout wait (the in-flight builds may still complete). Stop immediately when the pool is provably dead, or when the general connect budget is exhausted.

      Parameters:
      connectAttempts - total connect attempts so far (1-based after failure)
      timeoutConnectAttempts - timeout-style attempts so far
      timedOut - true when the failure classified as a connect timeout
      poolDown - true if poolIsDefinitivelyDown() was true
      poolBuilding - true when poolState() reports builds in flight
      browserClosed - true if the browser already hung up (abort now)
      Returns:
      true to sleep and retry the outer connect loop
    • shouldOuterRetryConnect

      static boolean shouldOuterRetryConnect(int connectAttempts, int timeoutConnectAttempts, boolean timedOut, int poolState, boolean deadlineExpired)
      Whether the outer HTTP connect loop should retry after a failure, given the single pool-state snapshot taken for that failure.

      Both derived flags (poolDown, poolBuilding) come from one poolState() reading so a failure never mixes two pool observations — the reflective pool query is relatively expensive and one failure must not repeat it. Stops immediately when the shared request deadline has passed, regardless of remaining attempt budget.

      Parameters:
      connectAttempts - total connect attempts so far (1-based after failure)
      timeoutConnectAttempts - timeout-style attempts so far
      timedOut - true when the failure classified as a connect timeout
      poolState - the single poolState() reading for this failure
      deadlineExpired - true when the shared request deadline has passed
      Returns:
      true to sleep and retry the outer connect loop
    • connectDeadlineFrom

      static long connectDeadlineFrom(long nowMs)
      Compute the shared connect deadline for a request starting now.
      Parameters:
      nowMs - current time in ms since the epoch
      Returns:
      the absolute deadline (nowMs + REQUEST_CONNECT_DEADLINE_MS)
    • isDeadlineExpired

      static boolean isDeadlineExpired(long deadlineMs, long nowMs)
      Whether a shared connect deadline has passed. Pure decision — no clock access, safe for unit tests. With NO_DEADLINE this is always false, so callers that have not adopted the shared budget keep their original behavior.
      Parameters:
      deadlineMs - absolute deadline in ms since the epoch
      nowMs - current time in ms since the epoch
      Returns:
      true when nowMs has reached or passed the deadline
    • clampToDeadlineMs

      static long clampToDeadlineMs(long requestedMs, long deadlineMs, long nowMs)
      Clamp a requested timeout to the time left before a shared deadline. Used for naming lookups, retry sleeps, and per-leg connect timeouts so no single step can spend budget the rest of the request still needs. Pure decision. No clock access, safe for unit tests.
      Parameters:
      requestedMs - the timeout the step would use with no deadline
      deadlineMs - absolute deadline in ms since the epoch, or NO_DEADLINE
      nowMs - current time in ms since the epoch
      Returns:
      the requested timeout reduced to the remaining budget; 0 when the deadline has passed (callers must skip the step)
    • legConnectTimeoutMs

      static long legConnectTimeoutMs(long requestedMs, long deadlineMs, long nowMs)
      Compute the connect timeout to apply to one failover leg under a shared deadline. Pure decision. No clock access, safe for unit tests.

      Three behaviors the raw clamp lacked: a caller that requested no timeout at all (requestedMs <= 0, which streaming can report when its effective connect window is not yet established) gets DEFAULT_CONNECT_TIMEOUT instead of waiting forever against a wall-clock budget; the result is clamped to the remaining budget; and a budget smaller than MIN_CONNECT_LEG_MS returns -1 so the walk skips the leg rather than starting an attempt streaming will cut short anyway. With NO_DEADLINE the requested value is passed through untouched, preserving legacy behavior for callers that have not adopted the shared budget.

      Parameters:
      requestedMs - the leg timeout the options carry; <= 0 means no timeout requested
      deadlineMs - absolute deadline in ms since the epoch, or NO_DEADLINE
      nowMs - current time in ms since the epoch
      Returns:
      the timeout to apply to the options, or -1 to skip this leg
    • extendDeadlineForBuildingPool

      static long extendDeadlineForBuildingPool(long deadlineMs, long nowMs, int poolState, boolean alreadyExtended)
      One-shot deadline extension for a request whose outbound tunnel pool is still building when the shared deadline runs out. Pure decision. No clock access, safe for unit tests.

      Pool state 0 means the pool exists with no valid tunnel yet but builds are in progress: no connect leg can succeed until one lands, and a healthy first build finishes well inside the grace window. Without this, a request racing a cold pool died at the full budget a few seconds before its pool came up — the observed SYN-timeout failure mode. The extension is granted at most once per request so the worst case stays bounded by REQUEST_CONNECT_DEADLINE_MS + POOL_BUILD_DEADLINE_GRACE_MS, and only when the deadline has actually expired (an expiring request with a healthy pool still fails on schedule).

      Parameters:
      deadlineMs - current deadline in ms since the epoch
      nowMs - current time in ms since the epoch
      poolState - latest poolState() reading (0 = still building)
      alreadyExtended - true if this request already consumed its grace
      Returns:
      the extended deadline, or deadlineMs unchanged when no extension applies
    • isConnectTimeout

      static boolean isConnectTimeout(Throwable e)
      For timeout wording.
      Parameters:
      e - the throwable whose cause chain, up to eight links deep, is scanned
      Returns:
      true if the cause chain looks like a connect/read timeout
    • poolIsDefinitivelyDown

      protected boolean poolIsDefinitivelyDown()
      And none are being built, so further connect retries cannot succeed.
      Returns:
      true if the client outbound tunnel pool provably has no tunnels
    • poolState

      protected int poolState()
      Router-context only, best-effort check of the client outbound tunnel pool. Uses reflection so i2ptunnel compiles against core alone. The router creates a per-client pool keyed by session hash; getValidTunnelCount() counts non-failed, non-expired tunnels, getInProgressCount() counts builds in progress.
      Returns:
      1 if the pool has valid tunnels, 0 if it exists but is still building, -1 if it exists but is dead (no valid, none building), -2 if unknown (standalone client, no router pool)
    • run

      public void run()
      Non-final since 0.9.11. open will be true before being called. Any overrides must set listenerReady = true and then notifyAll() if setup is successful, and must call close() and then notifyAll() on failure or termination.
      Specified by:
      run in interface Runnable
    • createServerSocket

      private ServerSocket createServerSocket(Properties opts, InetAddress addr) throws IOException
      Create the local server socket, with SSL if enabled.
      Parameters:
      opts - the client options
      addr - the local bind address
      Returns:
      the bound server socket
      Throws:
      IOException - if the socket cannot be created
    • waitForStartRunning

      private void waitForStartRunning()
      Wait until startRunning() authorizes connection processing.
    • initializeExecutor

      private void initializeExecutor()
      Get this tunnel's private runner pool from the tunnel controller group (or create one locally if the group was never started). The pool is a share of the global clientRunnerMax budget so another dest's flood cannot consume every runner thread.
    • resolveMaxConnections

      static int resolveMaxConnections(String configured)
      Resolve the max-concurrent-connections cap from the tunnel's configured value.

      Pure decision - no context access, safe for unit tests. Returns the configured value when it is a positive integer, otherwise the built-in default. A configured value of 0 or negative is rejected so a typo cannot disable the flood-shedding cap.

      Parameters:
      configured - the raw integer string from PROP_MAX_CONNECTIONS, may be null
      Returns:
      the connection cap to use: the parsed value if positive, else DEFAULT_MAX_CONNECTIONS
    • getEffectiveMaxConnections

      int getEffectiveMaxConnections()
      The effective concurrent-connection cap for this tunnel at the moment the accept loop admits a connection. Reads the Tuner-managed default dynamically so the floor can move without a tunnel restart, unless this tunnel declared an explicit "i2ptunnel.maxConnections" override (which always wins).

      The Tuner raises this gate under sustained load so excess inbound connections are handed to executor threads instead of being shed (a shed close with zero bytes is what an HTTP proxy browser reports as an empty response), and lowers it back toward the floor when idle to bound memory/FD usage.

      Returns:
      the effective cap: >= 1 when gated, 0 meaning unlimited
    • resolveEffectiveMaxConnections

      static int resolveEffectiveMaxConnections(boolean customized, int ownCap, int globalDefault)
      Pure decision helper for getEffectiveMaxConnections(): pick the per-tunnel override when one was explicitly configured, otherwise the Tuner-managed default cap. Package-visible and context-free so unit tests can exercise it without a live tunnel.
      Parameters:
      customized - whether this tunnel declared an explicit "i2ptunnel.maxConnections" override
      ownCap - the tunnel's resolved cap (resolveMaxConnections(String) result)
      globalDefault - the Tuner-managed default cap
      Returns:
      the effective cap: >= 1 when gated, 0 meaning unlimited
    • resolveRunnerCeiling

      static int resolveRunnerCeiling(boolean customized, int effectiveMax)
      Ceiling to pin on this tunnel's runner pool at creation time.

      Customized tunnels keep their explicit cap; un-customized tunnels pass 0 so TunnelControllerGroup resolves the live Tuner default on each rebalance rather than freezing the start-time value (ceiling staleness).

      Parameters:
      customized - true when the tunnel set its own maxConnections
      effectiveMax - the tunnel's resolved effective maxConnections
      Returns:
      the explicit ceiling, or 0 to track the live default
    • manageConnection

      protected void manageConnection(Socket s)
      Manage the connection just opened on the specified socket.
      Parameters:
      s - Socket to take care of
    • writeShedResponse

      protected void writeShedResponse(Socket s)
      Best-effort shed response on the browser-facing socket, then close it.

      The default is a silent close (non-HTTP tunnels). HTTP subclasses override this to emit a real status line. A cookie-capped meta-refresh page or a 503 — so browsers never see an empty response for a shed connection.

      Parameters:
      s - the accepted socket being shed; never null
    • close

      public boolean close(boolean forced)
      Note that the tunnel can be reopened after this by calling startRunning(). This may not release all resources. In particular, the I2PSocketManager remains and it may have timer threads that continue running.

      To release all resources permanently, call destroy().

      Does nothing if open is already false. Sets open = false but does not notifyAll().

      Specified by:
      close in class I2PTunnelTask
      Parameters:
      forced - true to interrupt an in-progress build, false to close gracefully
      Returns:
      true if the tunnel is closed, or false if connections were still active and the close was refused
    • getSocketOpenTimeout

      public long getSocketOpenTimeout()
      Returns the socket open timeout in ms; close() will proceed after this even with active sockets.
      Returns:
      the socket open timeout in milliseconds
    • destroy

      public boolean destroy()
      Note that the tunnel cannot be reopened after this by calling startRunning(), as it will destroy the underlying socket manager. This releases all resources if not a shared client. For shared client, the router will kill all the remaining streaming timers at shutdown.
      Overrides:
      destroy in class I2PTunnelTask
      Returns:
      true if every resource was released
    • closeSocket

      public static void closeSocket(Socket s)
      Close a socket silently.
      Parameters:
      s - the socket to close
    • clientConnectionRun

      protected abstract void clientConnectionRun(Socket s)
      Manage a connection in a separate thread. This only works if you do not override manageConnection(Socket).

      This is run in a thread from an unlimited-size thread pool, so it may block or run indefinitely.

      Parameters:
      s - the socket to manage