java.lang.Object
net.i2p.client.streaming.impl.ConnectionHandler

class ConnectionHandlerextends Object
Receive new connection attempts

Use a bounded queue to limit the damage from SYN floods, router overload, or a slow client.

  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    private class 
    Worker that pulls SYNs from the accept queue, processes them, and puts resulting connections into the result queue.
    private static class 
    Simple end-of-queue marker.
    (package private) static class 
    Accept-queue bookkeeping for one enqueued SYN.
    private class 
    Periodic reaper that removes SYN packets from the accept queue after their accept timeout has elapsed, sending a reset if it was a SYN.
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    private int
    Explicit per-manager override, or -1 to use the live configured getAcceptTimeout().
    private final List<Thread>
    The threads accepting connections.
    private boolean
    True while the handler accepts work.
    private final AtomicInteger
    Workers currently handling a connection.
    private final ByteCache
    Cache supplying the packet buffers.
    private final I2PAppContext
    The app context.
    private long
    Router-clock time of the last tunnel-stress sample.
    private long
    Router-clock time of the last SYN expire-rate sample.
    private final Log
    Logger for the connection handler.
    private final ConnectionManager
    The manager owning this handler.
    private boolean
    True to restart the handler after the current pass.
    The connections waiting for a worker.
    Enqueue records for packets currently in (or just polled from) the accept queue, keyed by packet identity.
    Packets waiting to be sent.
    private final LongAdder
    SYNs that expired un-accepted (removed by SynReaper) in the current window.
    private final LongAdder
    SYNs added to the acceptance queue within the current sample window.
    private int
    Most recently observed SYN accept-queue residence time (ms), i.e.
    private final SimpleTimer2
    Timer running the reaper tasks.
    private double
    Cached tunnel build success fraction; NaN when unavailable.
    private final AtomicInteger
    Sequence number of the worker threads.
    private static final int
    Default number of accept worker threads.
    private static final int
    Default max SYN queue size — large enough to absorb bursts.
    private static final int
    Ceiling for scale-up under SYN queue pressure, see ConnectionHandler.SynReaper.
    (package private) static final int
    Baseline recent-SYN expire rate (percent) that must be exceeded, along with low build success, before the clamp is armed.
    (package private) static final int
    Default RTT scale factor: the adaptive SYN floor is scale * recentRTT (capped at the configured timeout) whenever a positive stall is detected.
    (package private) static final int
    Minimum SYN accept-queue timeout (ms) kept even on a genuinely stressed, fast fabric.
    private static final long
    Minimum interval between re-sampling tunnel build success (ms).
    (package private) static final double
    Build success fraction below which the tunnel system is considered stressed.
    (package private) static final long
    Interval between SYN accept-queue sweeps (ms).
  • Constructor Summary

    Constructors
    Constructor
    Description
    Creates a new instance of ConnectionHandler.
  • Method Summary

    Modifier and Type
    Method
    Description
    accept(long timeoutMs)
    Receive an incoming connection.
    private int
    Live configured accept timeout; re-read each SYN so config changes apply without a restart.
    private int
    Number of accept worker threads.
    boolean
    Check if this handler is actively accepting new connections.
    (package private) static int
    getAdaptiveSynTimeout(int configuredTimeoutMs, double buildSuccess, int recentExpireRatePct, int rttMs)
    Compute the effective SYN accept-queue timeout for an inbound SYN.
    private int
    The effective SYN accept-queue timeout: the configured timeout, clamped -- only while the tunnel system shows positive stall evidence -- to an RTT-aware floor never below SYN_STRESS_MIN_TIMEOUT and never above the configured timeout.
    private int
    This is both SYNs and subsequent packets, and with an initial window size of 12, this is a backlog of 5 to 64 Syns, which seems like plenty for now Don't make this too big: removal of an expired entry is O(queue size), so a sweep that reaps the whole queue is O(n**2) - sortof.
    (package private) int
    Returns the max queue size for Tuner integration.
    (package private) int
    Returns the current SYN accept queue depth for Tuner integration.
    private int
    A recent round-trip time sample in milliseconds, or 0 when unavailable.
    private int
    Recent SYN expire rate, sampled on the same interval as build success.
    private double
    Tunnel build success fraction, sampled at most once per SYN_STRESS_SAMPLE_INTERVAL to avoid per-packet StatManager lookups (each SystemVersion probe is six RateStat reads).
    private void
    publishSynExpireRate(int ratePct)
    Publish the SYN accept-queue expire rate as a router-wide RateStat so the Tuner can distinguish latency-bound accept stalls (high expire rate: more handler threads will not help.
    void
    Receive new connection attempts
    private void
    We found a non-SYN packet that was queued in the syn queue, check to see if it has a home now, else drop it ...
    private void
    Re-send a SYN-ACK in response to a retransmitted SYN for an existing connection.
    (package private) static int
    resolveAcceptTimeout(int overrideMs, int configDefaultMs)
    Resolve the effective SYN accept-queue timeout from an explicit override and the live configured value.
    private void
    Record how long a freshly accepted SYN spent in the accept queue.
    private void
    sendReset(Packet packet)
    Send a reset in response to this packet, but only if it contains a FROM field and Signature that can be verified.
    (package private) void
    Max time a received SYN may sit in the accept queue before accept(long) is expected to pull it.
    void
    setActive(boolean active)
    Whether this handler is actively accepting new connections.
    void
    The router told us it's going to restart.
    private void
    Stop all accept worker threads.
    private void
    Start one accept worker, named uniquely.
    private void
    Start the accept worker threads.
    (package private) static int
    synExpireRatePct(int processed, int expired)
    The expire rate of one sample window: percent of queued SYNs that were removed by SynReaper without ever being accepted.
    (package private) static String
    synExpiryMessage(Packet syn, int queueDepth, int maxQueueSize, int timeoutMs)
    Full single-line DEBUG detail for a timed-out SYN, folding in both the packet detail and the queue/timeout pressure that caused the expiry.
    (package private) static void
    Build a diagnostic message for a SYN that expired on the accept queue without being accepted.

    Methods inherited from class Object

    clone, equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

    • _context

      private final I2PAppContext _context
      The app context.
    • _log

      private final Log _log
      Logger for the connection handler.
    • _cache

      private final ByteCache _cache
      Cache supplying the packet buffers.
    • _manager

      private final ConnectionManager _manager
      The manager owning this handler.
    • _synQueue

      private final LinkedBlockingDeque<Packet> _synQueue
      Packets waiting to be sent.
    • _resultQueue

      private final LinkedBlockingQueue<Connection> _resultQueue
      The connections waiting for a worker.
    • _acceptWorkers

      private final List<Thread> _acceptWorkers
      The threads accepting connections.
    • _activeWorkerCount

      private final AtomicInteger _activeWorkerCount
      Workers currently handling a connection.
    • _workerId

      private final AtomicInteger _workerId
      Sequence number of the worker threads.
    • _timer

      private final SimpleTimer2 _timer
      Timer running the reaper tasks.
    • _active

      private volatile boolean _active
      True while the handler accepts work.
    • _acceptTimeout

      private volatile int _acceptTimeout
      Explicit per-manager override, or -1 to use the live configured getAcceptTimeout().
    • _restartPending

      private boolean _restartPending
      True to restart the handler after the current pass.
    • SYN_STRESS_THRESHOLD

      static final double SYN_STRESS_THRESHOLD
      Build success fraction below which the tunnel system is considered stressed.
      See Also:
    • SYN_STRESS_MIN_TIMEOUT

      static final int SYN_STRESS_MIN_TIMEOUT
      Minimum SYN accept-queue timeout (ms) kept even on a genuinely stressed, fast fabric.
      See Also:
    • SYN_RTT_SCALE_DEFAULT

      static final int SYN_RTT_SCALE_DEFAULT
      Default RTT scale factor: the adaptive SYN floor is scale * recentRTT (capped at the configured timeout) whenever a positive stall is detected. 4 keeps a handshake alive through roughly four round-trips of server/queue latency, matching live 3-10s-RTT fabrics while still failing fast on genuinely dead fast tunnels. Tunable via I2PSocketManagerFull.setRttSynTimeoutScale(int).
      See Also:
    • SYN_EXPIRE_THRESHOLD_DEFAULT

      static final int SYN_EXPIRE_THRESHOLD_DEFAULT
      Baseline recent-SYN expire rate (percent) that must be exceeded, along with low build success, before the clamp is armed. 100 disables the clamp.
      See Also:
    • SYN_STRESS_SAMPLE_INTERVAL

      private static final long SYN_STRESS_SAMPLE_INTERVAL
      Minimum interval between re-sampling tunnel build success (ms).
      See Also:
    • SYN_SWEEP_INTERVAL

      static final long SYN_SWEEP_INTERVAL
      Interval between SYN accept-queue sweeps (ms). Far shorter than any accept timeout, so expiry latency is dominated by the timeout itself.
      See Also:
    • DEFAULT_MAX_QUEUE_SIZE

      private static final int DEFAULT_MAX_QUEUE_SIZE
      Default max SYN queue size — large enough to absorb bursts.
      See Also:
    • DEFAULT_ACCEPT_WORKERS

      private static final int DEFAULT_ACCEPT_WORKERS
      Default number of accept worker threads. One worker cannot drain a queue thousands deep on a busy public server (tracker, mail), so every SYN behind it waits the full accept timeout.
      See Also:
    • MAX_ACCEPT_WORKERS

      private static final int MAX_ACCEPT_WORKERS
      Ceiling for scale-up under SYN queue pressure, see ConnectionHandler.SynReaper.
      See Also:
    • _lastStressSampleAt

      private volatile long _lastStressSampleAt
      Router-clock time of the last tunnel-stress sample.
    • _lastSynExpireSampleAt

      private volatile long _lastSynExpireSampleAt
      Router-clock time of the last SYN expire-rate sample. Kept separate from _lastStressSampleAt because each sampler has its own window and one must not refresh the other's (that would starve this window's rollover: getSynExpireRatePct() would never publish).
    • _tunnelBuildSuccess

      private volatile double _tunnelBuildSuccess
      Cached tunnel build success fraction; NaN when unavailable.
    • _synQueueProcessed

      private final LongAdder _synQueueProcessed
      SYNs added to the acceptance queue within the current sample window. A LongAdder because the accept workers (4-16 of them) and the SynReaper all increment it; a volatile int with ++ is not atomic and the window silently lost updates, which is what the adaptive clamp below is built on.
    • _synQueueExpired

      private final LongAdder _synQueueExpired
      SYNs that expired un-accepted (removed by SynReaper) in the current window. Same multi-writer reasoning as _synQueueProcessed.
    • _synQueueResidenceMs

      private volatile int _synQueueResidenceMs
      Most recently observed SYN accept-queue residence time (ms), i.e. how long a fresh SYN waited in the queue before being accepted. 0 until the first acceptance.
    • _synEnqueueTimes

      private final ConcurrentHashMap<Packet, ConnectionHandler.SynEntry> _synEnqueueTimes
      Enqueue records for packets currently in (or just polled from) the accept queue, keyed by packet identity. Value holds the enqueue clock-time and the accept timeout snapshot taken at that moment; a worker that polls a SYN refreshes the entry so the expiry matches the timeout the client was quoted. Swept by SynReaper; re-arming is O(1) per SYN instead of the two per-packet timer events of the old TimeoutSyn design.
  • Constructor Details

    • ConnectionHandler

      public ConnectionHandler(I2PAppContext context, ConnectionManager mgr, SimpleTimer2 timer)
      Creates a new instance of ConnectionHandler.
      Parameters:
      context - the application context supplying the clock and logging
      mgr - the connection manager owning this handler and its queues
      timer - the shared timer used to schedule SYN expiry sweeps
  • Method Details

    • setAcceptTimeout

      void setAcceptTimeout(int ms)
      Max time a received SYN may sit in the accept queue before accept(long) is expected to pull it. If accept() is delayed (busy or slow hosted server), the queued SYN is reset after this window, so it must be long enough to absorb brief stalls without refusing inbound connections. Tunable via i2p.streaming.acceptTimeout (default: 60000 / 60 seconds). On a congested fabric RTT is commonly 3-10s, so 30s was too short once a SYN actually queued; 60s gives the RTT-aware floor (see getAdaptiveSynTimeout(int, double, int, int)) headroom to extend on slow-but-alive tunnels without refusing them. The configured value is re-read on every SYN, so changes apply without a restart unless this override is set.
      Parameters:
      ms - an explicit override, or < 0 to return to the live configured value
    • getAcceptTimeout

      private int getAcceptTimeout()
      Live configured accept timeout; re-read each SYN so config changes apply without a restart.
      Returns:
      the i2p.streaming.acceptTimeout property in ms, 60000 if unset
    • getMaxQueueSize

      private int getMaxQueueSize()
      This is both SYNs and subsequent packets, and with an initial window size of 12, this is a backlog of 5 to 64 Syns, which seems like plenty for now Don't make this too big: removal of an expired entry is O(queue size), so a sweep that reaps the whole queue is O(n**2) - sortof. Read dynamically from config or Tuner — no restart required.
      Returns:
      the max queue size
    • getAcceptWorkerCount

      private int getAcceptWorkerCount()
      Number of accept worker threads. Dynamically adjustable via Tuner.
      Returns:
      the number of accept worker threads
    • getAdaptiveSynTimeout

      static int getAdaptiveSynTimeout(int configuredTimeoutMs, double buildSuccess, int recentExpireRatePct, int rttMs)
      Compute the effective SYN accept-queue timeout for an inbound SYN.

      A queued SYN is reset via SynReaper after this window, so it bounds how long a client waits for a connection the server never got around to accepting. A fixed low clamp (e.g. the historical 10s) fails fast on genuinely dead tunnels but also expires slow-but-alive handshakes on a high-latency / congested fabric, which surfaces as empty pages to every client. So the clamp is made evidence-gated and RTT-aware:

      Gate (#4). The clamp fires only when there is positive evidence of a real stall, not mere latency: buildSuccess below SYN_STRESS_THRESHOLD and the recent SYN expire rate (recentExpireRatePct) at or above the Tuner threshold (I2PSocketManagerFull.getSynExprExpireThresh(), default 60). A server that is draining its SYN queue — even on a slow tunnel — is treated as healthy and keeps the full configured window.

      RTT-aware floor (#2). When the clamp does fire, the window is not a flat constant: the floor rises with the sampled round-trip time rttMs. On a fast fabric the floor is SYN_STRESS_MIN_TIMEOUT (true fast-fail); on a slow fabric it is scale * rttMs, so a slow-but-alive tunnel keeps substantially longer than the historical 10s and its handshake has room to complete. The result is always capped at the configured timeout and never exceeds it.

      No data never triggers the clamp: a NaN build success (stand-alone streaming or early startup) returns the configured timeout unchanged, an unknown expire rate (< 0) is treated as "no evidence" (no clamp), and a non-positive configured timeout is returned unchanged. A fractional 0.0 build success is genuine failure, not "no data".

      Parameters:
      configuredTimeoutMs - the configured accept timeout (i2p.streaming.acceptTimeout)
      buildSuccess - tunnel build success as a fraction [0.0, 1.0]; NaN when unavailable
      recentExpireRatePct - percent of recent SYN-queue entries that expired un-accepted, or a negative value when the rate is not known yet
      rttMs - a recent round-trip time sample in milliseconds, or <=0 when unavailable
      Returns:
      the timeout (ms) to arm the SYN reap with
    • getTunnelBuildSuccess

      private double getTunnelBuildSuccess()
      Tunnel build success fraction, sampled at most once per SYN_STRESS_SAMPLE_INTERVAL to avoid per-packet StatManager lookups (each SystemVersion probe is six RateStat reads).

      Outside a router context (stand-alone streaming) there are no tunnel statistics at all. Report NaN so the configured timeout is kept. SystemVersion reports 0% both for "no tunnel events yet" and for real failure, so 0 is treated as unknown (NaN) rather than stress.

      Returns:
      the build success fraction [0.0, 1.0], or NaN when unavailable
    • getSynExpireRatePct

      private int getSynExpireRatePct()
      Recent SYN expire rate, sampled on the same interval as build success.

      Counts SYNs added to the accept queue (_synQueueProcessed) and SYNs later removed by SynReaper without being accepted (_synQueueExpired). The window resets whenever a full SYN_STRESS_SAMPLE_INTERVAL elapses, so the rate reflects the most recent tunnel-health window rather than the ever since startup.

      Until the first window completes there is no evidence, which is reported as -1 so the adaptive clamp never fires on startup.

      Returns:
      percent of queued SYNs that expired un-accepted [0,100], or -1 when not enough history has accumulated yet
    • synExpireRatePct

      static int synExpireRatePct(int processed, int expired)
      The expire rate of one sample window: percent of queued SYNs that were removed by SynReaper without ever being accepted.

      Pure and static so the input to the adaptive stress clamp can be tested without a running streaming handler.

      Parameters:
      processed - SYNs added to the accept queue during the window
      expired - of those, SYNs that expired un-accepted
      Returns:
      percent [0,100] of queued SYNs that expired, or -1 when the window holds no evidence yet, i.e. nothing was processed. Clamped at 100: a SYN enqueued in an earlier window and reaped in this one is counted as expired here without having been processed here, so the raw ratio can exceed 1.
    • publishSynExpireRate

      private void publishSynExpireRate(int ratePct)
      Publish the SYN accept-queue expire rate as a router-wide RateStat so the Tuner can distinguish latency-bound accept stalls (high expire rate: more handler threads will not help. The transport is the bottleneck) from a genuinely parallelizable load. Only meaningful in a router context, and only when a real rate is available.
      Parameters:
      ratePct - the expire rate percent, or -1 when there is no evidence
    • getEffectiveAcceptTimeout

      private int getEffectiveAcceptTimeout()
      The effective SYN accept-queue timeout: the configured timeout, clamped -- only while the tunnel system shows positive stall evidence -- to an RTT-aware floor never below SYN_STRESS_MIN_TIMEOUT and never above the configured timeout. The configured value is re-read on every call, so a router.config change or Tuner override applies without a restart.
      Returns:
      timeout in ms to arm the SYN reap with
    • resolveAcceptTimeout

      static int resolveAcceptTimeout(int overrideMs, int configDefaultMs)
      Resolve the effective SYN accept-queue timeout from an explicit override and the live configured value. A non-negative override (set via setAcceptTimeout(int), e.g. by I2PSocketManagerFull.setAcceptTimeout(long)) wins; otherwise the configured value — re-read on each call so a router.config change or Tuner override applies without a restart — is used. A negative override clears any earlier explicit value and falls back to the configuration.

      Pure decision helper, extracted so the override-vs-config precedence is unit-testable without a router context.

      Parameters:
      overrideMs - the explicit override, or < 0 for "not set"
      configDefaultMs - the live configured accept timeout
      Returns:
      the timeout in ms to arm the SYN reap with
    • getRttMs

      private int getRttMs()
      A recent round-trip time sample in milliseconds, or 0 when unavailable.

      On a hosted (server) connection there is no handshake RTT to read before the connection exists, so the adaptive floor uses the SYN accept-queue residence time as a direct proxy for how long the server takes to reach a freshly queued SYN: the window that must exceed this residence to avoid expiring slow handshakes. An unavailable/zero sample keeps the fixed SYN_STRESS_MIN_TIMEOUT floor and is always safe.

      Returns:
      a recent queue-residence RTT in milliseconds, or 0 when not known yet
    • sampleSynResidence

      private void sampleSynResidence(Packet syn)
      Record how long a freshly accepted SYN spent in the accept queue.

      Called from the accept loop immediately before a fresh SYN is handed to receiveConnection(). The enqueue timestamp (stamped in receiveNewSyn) is removed and the residence time becomes the sampled getRttMs() input to the adaptive SYN timeout. The sample is lightly smoothed toward the previous value so a single slow acceptance does not swing the floor all the way to configured on its own.

      Retransmitted and poison SYNs never reach this point, so only genuinely fresh handshakes are measured.

      Parameters:
      syn - the freshly de-queued SYN about to be accepted
    • setRestartPending

      public void setRestartPending()
      The router told us it's going to restart. Call instead of setActive(false).
    • setActive

      public void setActive(boolean active)
      Whether this handler is actively accepting new connections. When set to false, a poison packet is offered to wake any threads blocked in accept().
      Parameters:
      active - true to accept connections, false to stop
    • getActive

      public boolean getActive()
      Check if this handler is actively accepting new connections.
      Returns:
      true if accepting connections
    • startAcceptWorkers

      private void startAcceptWorkers()
      Start the accept worker threads. Called when setActive(true).
    • startAcceptWorker

      private void startAcceptWorker()
      Start one accept worker, named uniquely. Synchronized because the ConnectionHandler.SynReaper may scale the pool up while the app activates or deactivates the handler.
    • shutdownAcceptWorkers

      private void shutdownAcceptWorkers()
      Stop all accept worker threads. Called when setActive(false).
    • getQueueDepth

      int getQueueDepth()
      Returns the current SYN accept queue depth for Tuner integration.
      Returns:
      queue size / max queue size
    • getQueueCapacity

      int getQueueCapacity()
      Returns the max queue size for Tuner integration.
      Returns:
      the configured max queue size
    • receiveNewSyn

      public void receiveNewSyn(Packet packet)
      Receive new connection attempts

      Use a bounded queue to limit the damage from SYN floods, router overload, or a slow client.

      Parameters:
      packet - the incoming connection attempt, null to do nothing
    • accept

      Receive an incoming connection. Polls the result queue populated by accept worker threads.
      Parameters:
      timeoutMs - max amount of time to wait for a connection (if less than 1ms, wait indefinitely)
      Returns:
      the connection received, never null; a timeout or a shut-down handler is reported by throwing instead
      Throws:
      RouterRestartException - (extends I2PException) if the router is apparently restarting
      ConnectException - if the I2PServerSocket is closed, or if interrupted.
      SocketTimeoutException - if a timeout was previously set with setSoTimeout and the timeout has been reached.
    • reReceivePacket

      private void reReceivePacket(Packet packet)
      We found a non-SYN packet that was queued in the syn queue, check to see if it has a home now, else drop it ...
      Parameters:
      packet - the queued packet to re-route, logged and ignored if null
    • sendReset

      private void sendReset(Packet packet)
      Send a reset in response to this packet, but only if it contains a FROM field and Signature that can be verified.
      Parameters:
      packet - the incoming packet we're responding to
    • resendSynAck

      private void resendSynAck(Connection con, Packet syn)
      Re-send a SYN-ACK in response to a retransmitted SYN for an existing connection. This prevents the race condition where: 1. Client sends SYN, server creates connection, sends SYN-ACK 2. Client retransmits SYN before receiving SYN-ACK (RTT > RTO) 3. Server destroys the old connection (which the client already completed handshake on) 4. Client's data arrives on dead stream IDs - dropped forever

      Instead, we just re-send the SYN-ACK for the existing connection, which is the standard TCP behavior for retransmitted SYNs.

      Parameters:
      con - the existing connection to re-send the SYN-ACK for
      syn - the retransmitted SYN packet
    • synExpirySummary

      static void synExpirySummary(Packet syn, StringBuilder out)
      Build a diagnostic message for a SYN that expired on the accept queue without being accepted. The historical message ("Expired on the SYN queue: " + packet) printed only stream IDs and flags — no peer hash, no queue pressure, no timeout — so a flood of these read as identical trailing-colon lines that could not be attributed or rate-assessed.

      This surfaces the data needed to tell "one flooding dest" apart from "many legit clients the app never accepted":

      • the source dest hash (short base32 prefix), which is the peer that sent the SYN and what the per-dest flood gate keys on;
      • current queue depth vs max, so a spike is visible as near-full;
      • the effective accept timeout that expired it (evidence-gated clamp).

      Idempotent and side-effect free so it can be unit-tested; the appends to out let the same formatting be reused for other SYN expiry reports (e.g. DEBUG-level summaries) without duplicating the format.

      Parameters:
      syn - the expired SYN, described as "null SYN" when null
      out - the buffer receiving the packet description
    • synExpiryMessage

      static String synExpiryMessage(Packet syn, int queueDepth, int maxQueueSize, int timeoutMs)
      Full single-line DEBUG detail for a timed-out SYN, folding in both the packet detail and the queue/timeout pressure that caused the expiry. The WARN-level report is one aggregate per sweep in ConnectionHandler.SynReaper.
      Parameters:
      syn - the expired SYN, described as "null SYN" when null
      queueDepth - the accept queue depth at expiry, as the count of unaccepted SYNs
      maxQueueSize - the configured queue cap, 0 when uncapped
      timeoutMs - the accept window in ms that elapsed before this SYN expired
      Returns:
      the complete single-line WARN message