- All Implemented Interfaces:
Runnable, EventDispatcher
- Direct Known Subclasses:
I2PTunnelDCCServer, I2PTunnelHTTPServer, I2PTunnelIRCServer
I2PTunnelServer is the foundation for all I2P server tunnels that accept incoming I2P connections and forward them to local TCP services. It manages the lifecycle of I2PSocketManager, handles connection acceptance, and provides connection forwarding to configured local destinations.
Key Features:
- Accepts incoming I2P connections via
I2PServerSocket - Forwards connections to configurable local TCP destinations
- Supports SSL/TLS connections via
I2PSSLSocketFactory - Provides port-based destination mapping (targetForPort.xx)
- Implements connection filtering via
StatefulConnectionFilter - Uses thread pool for concurrent connection handling
Connection Flow:
- Client connects to I2P destination via I2PServerSocket
blockingHandle(I2PSocket)is called with the I2PSocket- Local TCP socket is created via
getSocket(Hash, int) - I2PTunnelRunner handles bidirectional data streaming
Subclasses: I2PTunnelHTTPServer, I2PTunnelIRCServer, and others extend this class to provide protocol-specific handling while reusing the core connection management infrastructure.
Nested Class Summary
Nested ClassesField Summary
Modifier and TypeFieldDescriptionprivate final AtomicIntegerActive connection count used by the gate above.protected ThreadPoolExecutorPool running the per-connection client handlers, taken from the TunnelControllerGroup so server load stays inside its own thread budget.private StatefulConnectionFilterFilter deciding which connections to accept.private final AtomicLongLast gate-rejection warning timestamp for rate limiting.protected final LogLogger for this tunnel server.private intConcurrent inbound connection cap, 0 = unlimited.private intPer-tunnel server handler pool cap, -1 = Tuner-managed default.private final Map<Integer, InetSocketAddress> The open client connections, by hash.private I2PSSLSocketFactorySSL factory for the client connections.protected TunnelControllerGroupThe controller group that owns this tunnel's runner pool, cached fromTunnelControllerGroup.getInstance()once inrun().protected booleanTrue for HTTP bidirectional server mode.(package private) static final longFallback average handler time (ms) when no recent blockingHandleTime sample is available for the drain estimate.private static final intDefault port of the client socket.private static final longDefault read timeout, negative for no timeout.private static final longDefault write timeout for the outbound I2P stream (ms).protected I2PServerSocketThe listening server socket, null until the tunnel is started.protected final LoggingClient-visible log channel: every lifecycle notice and warning reported to the I2P client (bad option values, unresolvable hosts, lingering connections).protected intLocal port a HTTP bidir subclass hands to its proxy, replacing theDEFAULT_LOCAL_PORTplaceholder.private final ObjectGuards the server socket and the running flag across shutdown.(package private) static final longReject only when a fully busy pool would need longer than this to drain the queue.private static final intConnection attempts before giving up.static final StringConfig key naming an alternate private key file.static final StringConfig key to cap concurrent inbound connections; 0 (default) = unlimited.static final StringConfig key to override the socket read timeout in ms; 0 (default) = shut down silent connections promptly (HTTP server falls back to SERVER_READ_TIMEOUT_GET).static final StringConfig key to cap this tunnel's private server handler pool in threads.static final StringConfig key to enable unique local address.static final StringConfig key to enable SSL.static final StringConfig key to override the outbound write timeout in ms (0 = use the streaming default, which can be the full 120s disconnect timeout with no ACK progress).protected longDefault timeout - override if desired.private static final longDelay before the short recovery attempt.private static final longDelay before recreating the server socket.private static final longMinimum interval between gate-rejection warnings.protected InetAddressRemote endpoint of the current connection, null for the listening server.protected intRemote port of the current connection, 0 for the listening server.private static final intDelay between the connection retries.protected static longServer ID for HTTP bidirectional proxies; not incremented, always 0.protected final ObjectPassed to each I2PTunnelRunner so connection teardown serializes on it.protected final I2PSocketManagerBuilds the outbound sockets used to reach each configured destination.protected final ObjectGuards lazy creation of _sslFactory.protected I2PTunnelTaskHTTP bidirectional server task, null for standard tunnel server.Constructor Summary
ConstructorsConstructorDescriptionI2PTunnelServer(InetAddress host, int port, File privkey, String privkeyname, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) Non-blocking: the key file is read and a disconnected socket manager built here; the session is only connected, and the server socket only opened, by startRunning().I2PTunnelServer(InetAddress host, int port, InputStream privData, String privkeyname, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) Non-blocking: a disconnected socket manager is built from the already-open key stream here; the session is only connected, and the server socket only opened, by startRunning().I2PTunnelServer(InetAddress host, int port, String privData, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) Non-blocking: the key is decoded and a disconnected socket manager is built here; the session is only connected, and the server socket only opened, bystartRunning().I2PTunnelServer(InetAddress host, int port, I2PSocketManager sktMgr, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) Non-blocking: wraps a socket manager the caller has already built and connected, and marks the server open, so the caller is responsible for the session rather than this constructor.Method Summary
Modifier and TypeMethodDescription(package private) static booleanacquireConnectionSlot(int maxConnections, AtomicInteger active) Decide whether a concurrent-connection slot is available and, if so, reserve it.private I2PSessionaddSubsession(I2PSocketManager sMgr, String alt) Add an alternate EdDSA_SHA512_Ed25519 subsession alongside a DSA_SHA1 primary session.private long60s average handler time in ms for the queue drain estimate, or 0 when no recent sample exists (caller falls back to the default).protected voidblockingHandle(I2PSocket socket) This is run in a thread from a limited-size thread pool viaHandler.run(), except for a standard server (this class, no extension, as determined in getUsePool()), it is run directly in the acceptor thread (see run()).private voidbuildSocketMap(Properties props) Update the ports map.private voidcheckOfflineExpiration(I2PSession session, String name, String detail) Check if the session's offline signature is about to expire.booleanclose(boolean forced) Note that the tunnel can be reopened after this by calling startRunning().private static voidClose an I2P socket silently, ignoring any exceptions.private voidWarning, blocks while connecting to router and building tunnels;private voidConnect the session and subsessions with retries.private I2PSocketManagercreateManager(InputStream privData) Create the socket manager from the private key data.private static SocketcreatePlainSocket(InetAddress host, int port, int timeout) Create a plain (non-SSL) socket connection.private SocketcreateUniqueLocalSocket(Hash from, InetAddress remoteHost, int remotePort, int timeout) Create a socket with a unique local address derived from the peer hash.booleandestroy()Note that the tunnel cannot be reopened after this by calling startRunning(), as it will destroy the underlying socket manager.(package private) static longeffectiveWriteTimeout(Properties props) Determine the write timeout to bind the outbound I2P stream to, in ms.private I2PSSLSocketFactoryGet or lazily create the SSL socket factory.longGet the read idle timeout for newly-created connections (in milliseconds).protected SocketCreates a TCP socket connection to the configured destination.protected SocketgetSocket(Hash from, InetAddress remoteHost, int remotePort) Creates a TCP socket connection to the specified destination.private SocketgetSocket(Hash from, InetAddress remoteHost, int remotePort, boolean forceNonSSL) Get a regular or SSL socket depending on config.Return the socket manager, or null if destroyed.protected StringgetSocketString(int incomingPort) Gets a human-readable string representation of the target socket.private voidinit(InetAddress host, int port, I2PTunnel tunnel) Opens the client socket and starts serving.voidoptionsUpdated(I2PTunnel tunnel) Update the I2PSocketManager.(package private) static intParse the per-tunnel server handler pool override.private voidSleep for 2 minutes and recreate the server socket.private booleanAttempt to recover the server socket with a shorter delay.protected voidrejectConnection(I2PSocket socket) Refuse an inbound connection when the gate cap is exceeded.protected voidrejectConnection(I2PSocket socket, boolean queueFull) Refuse an inbound connection with an accurate reason.protected voidRelease a reservation taken bytryAcquireConnection().(package private) static voidreleaseConnectionSlot(AtomicInteger active) Release a slot reserved byacquireConnectionSlot(int, AtomicInteger).voidrun()Runs the I2PTunnelServer to accept and handle incoming connections asynchronously.voidsetReadTimeout(long ms) Set the read idle timeout for newly-created connections (in milliseconds).(package private) static booleanshouldRejectOnQueue(int queueDepth, int capacity, int active, int threads) Back-compat overload when the caller has no handle-time sample yet.(package private) static booleanshouldRejectOnQueue(int queueDepth, int capacity, int active, int threads, long avgHandleMs) Decide whether a connection should be rejected because the handler queue is already drowning.private static voidsleepIgnoringInterrupt(long delayMs) Sleep for the given duration, ignoring interruption (but restoring the flag).voidStart running the I2PTunnelServer.protected booleanTry to reserve a slot in the concurrent-connection gate.private voidwarnQueueFull(ThreadPoolExecutor ex, int queueDepth, int active, int threads) Rate-limited queue-full warning with the real pool metrics that drove the gate (not the connection-slot counter, which is often 0 here).Methods inherited from class I2PTunnelTask
connected, disconnected, errorOccurred, getBooleanOption, getId, getTunnel, isOpen, reportAbuse, routerDisconnected, setId, setName, setTunnel, toStringModifier and TypeMethodDescriptionvoidconnected(I2PSession session) For tasks that don't callI2PTunnel.addSession(I2PSession)directly.voiddisconnected(I2PSession session) Called when a session is disconnected.voiderrorOccurred(I2PSession session, String message, Throwable error) Does nothing here.protected booleangetBooleanOption(String opt, boolean dflt) Read a boolean client option from the tunnel's properties.intgetId()The id of the client list entry for this task.The I2PTunnel this task runs inside.booleanisOpen()Whether the tunnel is running and accepting connections.voidreportAbuse(I2PSession session, int severity) Does nothing here.protected voidTell the tunnel that the router connection has dropped.voidsetId(int id) Record the client list entry id for this task.protected voidRecord the display name for this task.voidRebind this task to a different tunnel.toString()The display name given to this task when it was constructed.Methods inherited from class EventDispatcherImpl
attachEventDispatcher, detachEventDispatcher, getEventDispatcher, getEvents, getEventValue, ignoreEvents, notifyEvent, unIgnoreEvents, waitEventValueModifier and TypeMethodDescriptionvoidAttach an EventDispatcher object to the events dispatching chain.voidDetach the specified EventDispatcher object from the events dispatching chain.Object to be used to deliver events (usuallythis, but YMMV).Retrieve the names of all the events that have been received.getEventValue(String name) Retrieve the value currently associated with the specified event value.voidIgnore further event notifications.voidnotifyEvent(String eventName, Object args) Deliver an event.voidAlmost like the method above :-).waitEventValue(String name) Wait until the given event has received a value.
Field Details
_log
Logger for this tunnel server.sockMgr
Builds the outbound sockets used to reach each configured destination.i2pss
The listening server socket, null until the tunnel is started.lock
Guards the server socket and the running flag across shutdown.slock
Passed to each I2PTunnelRunner so connection teardown serializes on it.sslLock
Guards lazy creation of _sslFactory.remoteHost
Remote endpoint of the current connection, null for the listening server.remotePort
protected volatile int remotePortRemote port of the current connection, 0 for the listening server.l
Client-visible log channel: every lifecycle notice and warning reported to the I2P client (bad option values, unresolvable hosts, lingering connections)._sslFactory
SSL factory for the client connections.DEFAULT_READ_TIMEOUT
private static final long DEFAULT_READ_TIMEOUTDefault read timeout, negative for no timeout.- See Also:
readTimeout
protected volatile long readTimeoutDefault timeout - override if desired.PROP_USE_SSL
PROP_UNIQUE_LOCAL
Config key to enable unique local address.- See Also:
PROP_ALT_PKF
Config key naming an alternate private key file. When set and readable, an EdDSA_SHA512_Ed25519 subsession is added alongside a DSA_SHA1 primary session; the primary is left alone if it is already not DSA_SHA1.- See Also:
PROP_MAX_CONNECTIONS
Config key to cap concurrent inbound connections; 0 (default) = unlimited. When the cap is reached, new connections are rejected promptly (HTTP: 503) instead of being queued behind a saturated pool. This is the last-resort governor under duress; normal operation should not reach it. Key:tunnel.N.option.i2ptunnel.server.maxConnections.- See Also:
PROP_READ_TIMEOUT
Config key to override the socket read timeout in ms; 0 (default) = shut down silent connections promptly (HTTP server falls back to SERVER_READ_TIMEOUT_GET). Key:tunnel.N.option.i2ptunnel.server.readTimeout.- See Also:
DEFAULT_WRITE_TIMEOUT
private static final long DEFAULT_WRITE_TIMEOUTDefault write timeout for the outbound I2P stream (ms). Without a bounded value, a stalled peer (send window not advancing, e.g. under a SYN flood) holds a server handler thread for the full streaming disconnect timeout (default 120s) insideMessageOutputStream#flushorclose(). A bounded write timeout fails when there is no ACK progress instead of blocking the handler. It does not cap throughput for a peer that is progressing, so it is safe for legitimate slow transfers. 60s is long enough that a normal I2P RTO blip (~30s avgRTO) with no other forward-ACK progress does not fail the write, while still bounding a dead peer well under the 120s disconnect timeout. A tighter default (15s) was tried and false-failed mid-body downloads whenever a single lost ACK spanned the window.- See Also:
PROP_WRITE_TIMEOUT
Config key to override the outbound write timeout in ms (0 = use the streaming default, which can be the full 120s disconnect timeout with no ACK progress). Key:tunnel.N.option.i2ptunnel.server.writeTimeout.- See Also:
PROP_SERVER_THREADS
Config key to cap this tunnel's private server handler pool in threads. Each server tunnel owns a per-port pool summed under the globali2ptunnel.serverHandler.threadsbudget; unset or < 2 follows the Tuner-managed default (i2ptunnel.server.threads). Key:tunnel.N.option.i2ptunnel.server.threads.- See Also:
RECONNECT_DELAY_2MIN
private static final long RECONNECT_DELAY_2MINDelay before recreating the server socket.- See Also:
RECONNECT_DELAY_10S
private static final long RECONNECT_DELAY_10SDelay before the short recovery attempt.- See Also:
_clientExecutor
Pool running the per-connection client handlers, taken from the TunnelControllerGroup so server load stays inside its own thread budget. Created in run(); null until then._tcg
The controller group that owns this tunnel's runner pool, cached fromTunnelControllerGroup.getInstance()once inrun(). Cached because the singleton accessor synchronizes on the class object, which a per-request call would turn into contention across every tunnel thread._socketMap
The open client connections, by hash._filter
Filter deciding which connections to accept._maxConnections
private volatile int _maxConnectionsConcurrent inbound connection cap, 0 = unlimited. Read from PROP_MAX_CONNECTIONS._activeConnections
Active connection count used by the gate above._serverThreadOverride
private volatile int _serverThreadOverridePer-tunnel server handler pool cap, -1 = Tuner-managed default. Read from PROP_SERVER_THREADS; re-applied live inoptionsUpdated(I2PTunnel).task
HTTP bidirectional server task, null for standard tunnel server.bidir
protected boolean bidirTrue for HTTP bidirectional server mode.serverId
protected static volatile long serverIdServer ID for HTTP bidirectional proxies; not incremented, always 0.DEFAULT_LOCAL_PORT
private static final int DEFAULT_LOCAL_PORTDefault port of the client socket.- See Also:
localPort
protected int localPortLocal port a HTTP bidir subclass hands to its proxy, replacing theDEFAULT_LOCAL_PORTplaceholder. Unused by a standard server.RETRY_DELAY
private static final int RETRY_DELAYDelay between the connection retries.- See Also:
MAX_RETRIES
private static final int MAX_RETRIESConnection attempts before giving up.- See Also:
DEFAULT_HANDLE_MS
static final long DEFAULT_HANDLE_MSFallback average handler time (ms) when no recent blockingHandleTime sample is available for the drain estimate.- See Also:
MAX_QUEUE_DRAIN_MS
static final long MAX_QUEUE_DRAIN_MSReject only when a fully busy pool would need longer than this to drain the queue. Generous enough that normal bursts queue and complete (minimal drops) without pinning sockets indefinitely.- See Also:
REJECT_WARN_MS
private static final long REJECT_WARN_MSMinimum interval between gate-rejection warnings. @since 0.9.71+.- See Also:
_lastRejectWarn
Last gate-rejection warning timestamp for rate limiting. @since 0.9.71+.
Constructor Details
I2PTunnelServer
public I2PTunnelServer(InetAddress host, int port, String privData, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) Non-blocking: the key is decoded and a disconnected socket manager is built here; the session is only connected, and the server socket only opened, bystartRunning().- Parameters:
host- server hostname, kept as the remote host and named in the display nameport- server port, kept as the remote port and named in the display nameprivData- Base64-encoded private key data, format is specified inPrivateKeyFilel- logger receiving the client-visible notices and warningsnotifyThis- dispatcher notified of this server's events, e.g. openServerResulttunnel- owning tunnel, supplying the RouterContext, client options and host- Throws:
IllegalArgumentException- if the I2CP configuration is b0rked so badly that we cant create a socketManager
I2PTunnelServer
public I2PTunnelServer(InetAddress host, int port, File privkey, String privkeyname, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) Non-blocking: the key file is read and a disconnected socket manager built here; the session is only connected, and the server socket only opened, by startRunning(). An unreadable file posts openServerResult "error" and throws.- Parameters:
host- server hostname, kept as the remote host and named in the display nameport- server port, kept as the remote port and named in the display nameprivkey- file containing the private key data, format is specified inPrivateKeyFileprivkeyname- the name of the privKey file, just for loggingl- logger receiving the client-visible notices and warningsnotifyThis- dispatcher notified of this server's events, e.g. openServerResulttunnel- owning tunnel, supplying the RouterContext, client options and host- Throws:
IllegalArgumentException- if the I2CP configuration is b0rked so badly that we cant create a socketManager
I2PTunnelServer
public I2PTunnelServer(InetAddress host, int port, InputStream privData, String privkeyname, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) Non-blocking: a disconnected socket manager is built from the already-open key stream here; the session is only connected, and the server socket only opened, by startRunning(). The stream is not closed by this constructor.- Parameters:
host- server hostname, kept as the remote host and named in the display nameport- server port, kept as the remote port and named in the display nameprivData- stream containing the private key data, format is specified inPrivateKeyFileprivkeyname- the name of the privKey file, just for loggingl- logger receiving the client-visible notices and warningsnotifyThis- dispatcher notified of this server's events, e.g. openServerResulttunnel- owning tunnel, supplying the RouterContext, client options and host- Throws:
IllegalArgumentException- if the I2CP configuration is b0rked so badly that we cant create a socketManager
I2PTunnelServer
public I2PTunnelServer(InetAddress host, int port, I2PSocketManager sktMgr, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) Non-blocking: wraps a socket manager the caller has already built and connected, and marks the server open, so the caller is responsible for the session rather than this constructor.- Parameters:
host- server hostname, kept as the remote host and named in the display nameport- server port, kept as the remote port and named in the display namesktMgr- the existing socket managerl- logger receiving the client-visible notices and warningsnotifyThis- dispatcher notified of this server's events, e.g. openServerResulttunnel- owning tunnel, supplying the RouterContext, client options and host
Method Details
init
Opens the client socket and starts serving.- Parameters:
host- the host to bind toport- the port to bind totunnel- the tunnel to connect through
createManager
Create the socket manager from the private key data.- Parameters:
privData- the stream to read the destination and private key from- Returns:
- the new socket manager
- Throws:
IllegalArgumentException- if the I2CP configuration is b0rked so badly that we cant create a socketManager
addSubsession
Add an alternate EdDSA_SHA512_Ed25519 subsession alongside a DSA_SHA1 primary session.Runs only when
PROP_ALT_PKFnames a resolvable key file and the primary session's destination really is DSA_SHA1; a primary session of any other signature type is left alone. Any failure to read the file or add the subsession is logged and null is returned, leaving the primary session and its tunnel running without the alternate destination. The signature type is declared here rather than read back from the key file, so the nickname labels below assume an EdDSA key file.- Parameters:
sMgr- the primary session's socket manageralt- value ofPROP_ALT_PKF, a key file path- Returns:
- the subsession, or null if none was added
connectSessionWithRetry
private void connectSessionWithRetry()Connect the session and subsessions with retries.- Throws:
IllegalArgumentException- if max retries reached
connectManager
private void connectManager()Warning, blocks while connecting to router and building tunnels;- Throws:
IllegalArgumentException- if the I2CP configuration is b0rked so badly that we can't create a socketManager
startRunning
public void startRunning()Start running the I2PTunnelServer. Warning, blocks while connecting to router and building tunnels;- Throws:
IllegalArgumentException- if the I2CP configuration is b0rked so badly that we cant create a socketManager
checkOfflineExpiration
Check if the session's offline signature is about to expire. Throws if expiration is imminent, logs a warning if soon.- Parameters:
session- the I2P session to checkname- the tunnel name for log messagesdetail- extra text in log messages (e.g. " alternate destination")
setReadTimeout
public void setReadTimeout(long ms) Set the read idle timeout for newly-created connections (in milliseconds). After this time expires without data being reached from the I2P network, the connection itself will be closed.Less than or equal to 0 means forever. Default -1 (forever) as of 0.9.36 for standard tunnels, but extending classes may override. Prior to that, default was 5 minutes, but did not work due to streaming bugs.
Applies only to future connections; calling this does not affect existing connections.
- Parameters:
ms- socket read idle timeout in milliseconds; zero or less means no timeout, the -1 default
tryAcquireConnection
protected boolean tryAcquireConnection()Try to reserve a slot in the concurrent-connection gate. Call right after accept(), before dispatching the handler. When this returns false the connection must be rejected (rejectConnection) and never dispatched. The reserved slot is released byreleaseConnection(), which runs in the handler's finally block so it also fires on early returns and exceptions.- Returns:
- true if under the cap (or unlimited), false if the cap is exceeded
releaseConnection
protected void releaseConnection()Release a reservation taken bytryAcquireConnection(). Idempotent-safe only when paired with the acquire; must run exactly once per accepted connection.acquireConnectionSlot
Decide whether a concurrent-connection slot is available and, if so, reserve it. Pure decision, no router context: called by the accept loop right after accept(), before a handler thread is consumed. Cap 0 = unlimited = always available and no reservation is recorded. The caller MUST pair a successful reservation withreleaseConnectionSlot(AtomicInteger)(normally from the handler's finally block).- Parameters:
maxConnections- the configured cap; <= 0 means unlimitedactive- the live reservation counter shared by all connections of the tunnel- Returns:
- true if a slot was reserved (or the cap is unlimited); false if over the cap
releaseConnectionSlot
Release a slot reserved byacquireConnectionSlot(int, AtomicInteger). Clamped at zero so a config change mid-flight (cap turned on while unlimited connections are live) can never push the counter negative and permanently disable the gate.- Parameters:
active- the reservation counter shared by all connections of the tunnel
parseServerThreadOverride
Parse the per-tunnel server handler pool override. Pure decision: null or non-numeric values yield -1 (use the Tuner-managed default); values below the floor (< 2) are also treated as "no override" to matchTunnelControllerGroup.normalizeThreadOverride(int).- Parameters:
st- the raw option value; may be null- Returns:
- the override thread count, or -1 for the default
shouldRejectOnQueue
static boolean shouldRejectOnQueue(int queueDepth, int capacity, int active, int threads, long avgHandleMs) Decide whether a connection should be rejected because the handler queue is already drowning. Pure decision, no router context: called from the accept loop after accept() and before a handler thread is consumed.Admission policy (high throughput, minimal drops):
- Hard stop near queue capacity (would hit AbortPolicy anyway).
- If any handler thread is free, admit — the backlog drains as soon as work is submitted; rejecting while capacity exists only creates empty responses the browser reports as failures.
- Only when the pool is fully busy is a drain-time estimate applied:
queueDepth * avgHandleMs / threads. Reject only when that exceedsMAX_QUEUE_DRAIN_MS, so short bursts queue and drain instead of being dropped, while a hopeless backlog fails fast rather than pinning sockets for minutes.
- Parameters:
queueDepth- current queued task countcapacity- total queue capacity; <= 0 disables this gateactive- currently busy handler threadsthreads- pool max threads; <= 0 disables this gateavgHandleMs- average handler time in ms; <= 0 usesDEFAULT_HANDLE_MS- Returns:
- true if the connection should be rejected with a 503
shouldRejectOnQueue
static boolean shouldRejectOnQueue(int queueDepth, int capacity, int active, int threads) Back-compat overload when the caller has no handle-time sample yet. UsesDEFAULT_HANDLE_MSfor the drain estimate.- Parameters:
queueDepth- current queued task countcapacity- total queue capacity; <= 0 disables this gateactive- currently busy handler threadsthreads- pool max threads; <= 0 disables this gate- Returns:
- true if the connection should be rejected
rejectConnection
Refuse an inbound connection when the gate cap is exceeded. Base implementation closes the socket; I2PTunnelHTTPServer overrides to send a 503 first. Warnings are rate-limited so a flood of rejections cannot spam the log.- Parameters:
socket- the accepted but undelivered I2PSocket to reject
rejectConnection
Refuse an inbound connection with an accurate reason. Queue-depth rejects are not connection-cap rejects. Conflating them made every saturated-queue drop look like a maxConnections trip (and logged maxConnections=0 when the cap was unlimited).The queue-full metrics line is emitted by
warnQueueFull(ThreadPoolExecutor, int, int, int)with real pool active/max/queue values; this method only rate-limits and closes so a flood cannot spam the log.- Parameters:
socket- the accepted but undelivered I2PSocket to rejectqueueFull- true if the handler queue gate rejected, false for the connection cap
warnQueueFull
Rate-limited queue-full warning with the real pool metrics that drove the gate (not the connection-slot counter, which is often 0 here).- Parameters:
ex- the saturated handler poolqueueDepth- queued task count at reject timeactive- busy handler threads at reject timethreads- pool maximum at reject time
avgHandleMs
private long avgHandleMs()60s average handler time in ms for the queue drain estimate, or 0 when no recent sample exists (caller falls back to the default).- Returns:
- average blockingHandleTime, or 0 if unavailable
getReadTimeout
public long getReadTimeout()Get the read idle timeout for newly-created connections (in milliseconds).Less than or equal to 0 means forever. Default -1 (forever) as of 0.9.36 for standard tunnels, but extending classes may override. Prior to that, default was 5 minutes, but did not work due to streaming bugs.
- Returns:
- The read timeout used for connections
getSocketManager
Return the socket manager, or null if destroyed.- Overrides:
getSocketManagerin classI2PTunnelTask- Returns:
- the socket manager, or null
close
public boolean close(boolean forced) Note that the tunnel can be reopened after this by calling startRunning(). This does 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().- Specified by:
closein classI2PTunnelTask- Parameters:
forced- true to interrupt an in-progress build, false to close gracefully- Returns:
- true if the tunnel was closed successfully, false if connections exist and forced is false
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.- Overrides:
destroyin classI2PTunnelTask- Returns:
- true if every resource was released
optionsUpdated
Update the I2PSocketManager. And since 0.9.15, the target host and port.- Overrides:
optionsUpdatedin classI2PTunnelTask- Parameters:
tunnel- the tunnel whose client options were updated
effectiveWriteTimeout
Determine the write timeout to bind the outbound I2P stream to, in ms.A stalled peer (send window not advancing, e.g. under a SYN flood) otherwise holds a server handler thread for the full streaming disconnect timeout (default 120s) inside
MessageOutputStream#flushorclose(). Bounding the write timeout fails when there is no ACK progress rather than blocking the handler; it does not cap throughput for a peer that is progressing, so it is safe for legitimate slow transfers.If either the tunnel's own key (
PROP_WRITE_TIMEOUT) or the streaming standard key (I2PSocketOptions.PROP_WRITE_TIMEOUT) is present, honor it: return a negative value so the caller leaves the socket options' value untouched (explicit tuning wins, including 0 meaning the unbounded streaming default). Otherwise return the bounded default so unconfigured tunnels are protected.- Parameters:
props- the tunnel's client options; may be null- Returns:
- the write timeout in ms to apply, or negative to apply none
buildSocketMap
Update the ports map.- Parameters:
props- the tunnel options, targetForPort.N entries define the map
run
public void run()Runs the I2PTunnelServer to accept and handle incoming connections asynchronously.Accepts connections in a loop, submitting each to the thread pool via
CompletableFuture. Handles expected exceptions with retries and logs errors. On shutdown, gracefully terminates the pool.sleepIgnoringInterrupt
private static void sleepIgnoringInterrupt(long delayMs) Sleep for the given duration, ignoring interruption (but restoring the flag).- Parameters:
delayMs- the time to sleep in milliseconds
reconnectServerSocket
private void reconnectServerSocket()Sleep for 2 minutes and recreate the server socket.recoverServerSocket
private boolean recoverServerSocket()Attempt to recover the server socket with a shorter delay. On failure, destroy the tunnel.- Returns:
- true if recovered, false if gave up
closeSilently
Close an I2P socket silently, ignoring any exceptions.- Parameters:
s- the socket to close, null is ignored
blockingHandle
This is run in a thread from a limited-size thread pool viaHandler.run(), except for a standard server (this class, no extension, as determined in getUsePool()), it is run directly in the acceptor thread (see run()).Handles an incoming I2P connection by forwarding it to the local TCP destination.
This method is called by the server socket's acceptance thread when a new I2P connection arrives. It creates a local TCP socket connection and spawns an I2PTunnelRunner to handle bidirectional data streaming between the I2P and TCP connections.
Implementation Notes:
- Subclasses that override this must call super.blockingHandle() or implement full connection handling themselves
- For protocol-specific handling (HTTP, IRC), spawn a handler thread and return
- For simple forwarding, this base implementation handles all cases
- Performance: local connections are fast enough to handle synchronously
Error Handling: SocketException and IOException are caught and logged, with the socket being reset to notify the peer of the failure.
- Parameters:
socket- the incoming I2P connection from a remote peer; never null- See Also:
getSocket
Creates a TCP socket connection to the configured destination.This method resolves the target host and port for the incoming connection, supporting port-based destination mapping via the client options. If a port has a specific mapping (targetForPort.xx=host:port), that destination is used; otherwise, the default remoteHost and remotePort are used.
Port Mapping Configuration:
targetForPort.80=localhost:8080 targetForPort.443=localhost:8443
Special Ports: Ports 443 and 22 are automatically flagged for non-SSL handling to prevent SSL-over-SSL issues.
- Parameters:
from- the hash of the peer's destination; used for local address bindingincomingPort- the port on which the I2P connection was received- Returns:
- a connected TCP Socket to the appropriate local destination
- Throws:
IOException- if the socket cannot be created or connected- See Also:
getSocketString
Gets a human-readable string representation of the target socket.This method is used for logging and error messages when socket creation fails. It returns the configured destination address, taking into account any port-specific mappings that may have been configured.
The returned string is formatted as "host:port" without any protocol prefix.
- Parameters:
incomingPort- the incoming I2P connection port for lookup- Returns:
- the target host and port as a string, or default if not configured
- See Also:
getSocket
Creates a TCP socket connection to the specified destination.This convenience method delegates to getSocket() with forceNonSSL=false, allowing SSL configuration to be controlled by client options.
- Parameters:
from- the hash of the peer's destinationremoteHost- the target InetAddress to connect toremotePort- the target port to connect to- Returns:
- a connected TCP Socket
- Throws:
IOException- if the socket cannot be created or connected- See Also:
getOrCreateSSLFactory
Get or lazily create the SSL socket factory.- Returns:
- the shared SSL socket factory, created on first use
- Throws:
IOException- if the factory cannot be initialized, wrapping the underlying security exception
createUniqueLocalSocket
private Socket createUniqueLocalSocket(Hash from, InetAddress remoteHost, int remotePort, int timeout) throws IOException Create a socket with a unique local address derived from the peer hash.- Parameters:
from- the peer's destination hash, supplying the unique local address bytesremoteHost- the address to connect toremotePort- the port to connect totimeout- the connect timeout in milliseconds- Returns:
- a connected socket bound to a unique loopback address
- Throws:
IOException- if the socket cannot be bound or connected
createPlainSocket
Create a plain (non-SSL) socket connection.- Parameters:
host- the address to connect toport- the port to connect totimeout- the connect timeout in milliseconds- Returns:
- a connected socket
- Throws:
IOException- if the connection fails
getSocket
private Socket getSocket(Hash from, InetAddress remoteHost, int remotePort, boolean forceNonSSL) throws IOException Get a regular or SSL socket depending on config. The SSL config applies to all hosts/ports, unless forced off.- Parameters:
from- hash of the peer's destination for local address bindingremoteHost- the target host to connect toremotePort- the target port to connect toforceNonSSL- true to skip SSL even when the SSL option is set- Returns:
- a connected regular or SSL socket
- Throws:
IOException- if the connection fails