- All Implemented Interfaces:
Runnable, EventDispatcher
- Direct Known Subclasses:
I2PTunnelHTTPBidirProxy
Handles various request formats and forwards through I2P:
$method http://$site[$port]/$path $protocolVersion $method $path $protocolVersion\nHost: $site $method http://i2p/$b64key/$path $protocolVersion $method /$site/$path $protocolVersion (deprecated) $method /eepproxy/$site/$path $protocolVersion
CONNECT (HTTPS) supported since 0.9.11. Note: http://i2p/$b64key/... and /eepproxy/$site/... not recommended for browsers due to broken relative links and cookies. http://$b64key/... and http://$b64key.i2p/... unsupported (illegal characters). Rewrite as http://i2p/$b64key/...
Directs I2P-resolved sites to destinations, others to outproxy (typically "squid.i2p"). Supports HTTP and HTTPS only. GET and POST tested; other methods should work.
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionprivate static classRead the first line unbuffered, then subsequent lines.(package private) static final classRequest-scoped state machine for one speculative warm socket.Nested classes/interfaces inherited from class I2PTunnelHTTPClientBase
I2PTunnelHTTPClientBase.AuthResult, I2PTunnelHTTPClientBase.OnProxySuccess, I2PTunnelHTTPClientBase.OnTimeoutModifier and TypeClassDescriptionprotected static enumAuthentication result status for HTTP client connections.protected classCallback for successful proxy connections.protected classCallback for timeout events.Field Summary
Modifier and TypeFieldDescriptionprivate static final ConcurrentHashMap<Hash, AtomicInteger> Active outbound I2P socket count per destination hash.private final StringUsed to protect actions via http://proxy.i2p/private static final ConcurrentHashMap<Hash, long[]> Per-remote-dest race token bucket for empty-response dual-race opens.private final ConcurrentHashMap<String, String> Map of host name to base64 destination for destinations collected via address helper links.static final StringHTTP authentication realm for HTTP proxy.(package private) static final intHow long to wait for another request on the same socket.private static final intDefault reconnects on an empty upstream response before giving up.private static final booleanDefault setting for gzipping responses.static final StringDefault jump server URLs.private static final booleanDefault keep-alive setting for browser connections.private static final booleanDefault keep-alive setting for I2P connections.private static final StringError response for an address helper key conflict.private static final StringError response for a new address helper host.private static final StringError response for an unresolvable helper key.private static final StringError response for a non-HTTP request protocol.private static final StringError response for an invalid request URI.private static final StringError response for a blacklisted address.private static final StringError response when address helpers are disabled.private static final StringError response for SSL requested to an I2P address.private static final StringError response for a localhost request through the proxy.private static final StringError response when no outproxy is configured.private static final StringThese are backups if the xxx.ht error page is missing.private static final StringURL parameter carrying an address helper.(package private) static final intTotal connect attempts before the client stops retrying and fails the request.(package private) static final longBackoff floor and per-attempt multiply base for I2P connect retries.private InternalSocketRunnerSocket runner for private mode, null otherwise.static final StringLocal HTTP proxy server hostname.private static final intPer-destination concurrent outbound connection limit.(package private) static final longIn-session b32/b33 naming lookup budget, clamped to the shared request connect deadline so naming cannot outlive the request.(package private) static final longNaming-service lookup budget for out-of-session and hostname lookups, clamped to the shared request connect deadline.static final StringConfig key: max reconnects on a zero-byte (empty) upstream response for GET/HEAD.static final StringConfig key to enable browser-side keepalive.static final StringConfig key to enable I2P-side keepalive.static final StringConfig key: false, the default, replaces the browser's Accept header with a generic value for the content types it names; true forwards it unchanged.static final StringConfig key to disable address helper.static final StringConfig key: false denies SSL to I2P destinations, but only once PROP_SSL_SET shows the user configured SSL by hand.static final StringConfig key for jump server list.static final StringAll default to false.static final StringConfig key: set when the user configures SSL by hand.private static final StringConfig key overriding the clearnet user agent.private static final StringConfig key overriding the I2P user agent.static final StringConfig key to enable sending User-Agent header.static final StringConfig key to enable sending Via header.private static final StringUser-Agent header sent to clearnet sites.private static final StringUser-Agent header sent to I2P sites.Fields inherited from class I2PTunnelHTTPClientBase
__requestId, _proxyList, BASIC_AUTH, BROWSER_READ_TIMEOUT, DEFAULT_READ_TIMEOUT, DIGEST_AUTH, ERR_DESTINATION_UNKNOWN, INITIAL_SO_TIMEOUT, PROP_AUTH, PROP_OUTPROXY_AUTH, PROP_OUTPROXY_PW, PROP_OUTPROXY_PW_PREFIX, PROP_OUTPROXY_USER, PROP_OUTPROXY_USER_PREFIX, PROP_PROXY_DIGEST_PREFIX, PROP_PROXY_DIGEST_SHA256_SUFFIX, PROP_PROXY_DIGEST_SUFFIX, PROP_PW, PROP_PW_PREFIX, PROP_SSL_OUTPROXIES, PROP_USE_OUTPROXY_PLUGIN, PROP_USER, SHED_COOKIE, SHED_MAX_REFRESH, SHED_READ_TIMEOUT_MS, SHED_REFRESH_SECONDS, SUCCESS_RESPONSEModifier and TypeFieldDescriptionprotected static final AtomicLongCounter for unique request IDs.List of configured outproxies.static final StringBasic auth type constant.protected static final intSocket read timeout for a browser fetch, four hours as a failsafe.protected static final int-1 (forever) as of 0.9.36, so that large POSTs won't timeout on the read side.static final StringDigest auth type constant.protected static final StringError response when destination is not found.protected static final intThis is a standard soTimeout, not a total timeout.static final StringAll auth.static final StringOutproxy auth property.static final StringOutproxy password property.static final StringOutproxy password property prefix.static final StringOutproxy username property.static final StringPasswords for specific outproxies may be added withoutproxyUsername.fooproxy.i2p=user andoutproxyPassword.fooproxy.i2p=pw.static final StringNew style MD5 auth.static final StringSHA-256 digest suffix.static final StringMD5 digest suffix.static final StringProxy password property.static final StringAdditional users may be added with proxyPassword.user=pw.static final StringProperty for SSL outproxies.static final StringProperty to use local outproxy plugin.static final StringProxy username property.(package private) static final StringCookie that counts meta-refresh retries on a shed connection.(package private) static final intHow many meta-refresh pages to serve before the final 503.(package private) static final intShort SO_TIMEOUT while reading the shed request head (never on the hot path).(package private) static final intRefresh delay in seconds for the intermediate shed page.protected static final StringHTTP 200 response for established CONNECT tunnels.Fields inherited from class I2PTunnelClientBase
_clientId, _context, _executor, _log, _ownDest, DEFAULT_CONNECT_TIMEOUT, DEFAULT_MAX_CONNECTIONS, DEFAULT_SOCKET_OPEN_TIMEOUT, dest, l, listenerReady, MAX_TIMEOUT_FAILOVER, MIN_CONNECT_LEG_MS, mySockets, NO_DEADLINE, POOL_BUILD_DEADLINE_GRACE_MS, PROP_MAX_CONNECTIONS, PROP_USE_SSL, REQUEST_CONNECT_DEADLINE_MS, sockLock, sockMgr, ssModifier and TypeFieldDescriptionprotected longThis client's ID.protected final I2PAppContextThe application context.protected ThreadPoolExecutorThread pool executor.protected final LogThe logger.protected booleanWhether we own our destination.(package private) static final longDefault connect timeout.static final intDefault cap on concurrently handled client connections.static final longDefault socket open timeout in ms, after whichI2PTunnelClientBase.close(boolean)will proceed even with active sockets.protected DestinationThe destination.protected final LoggingThe logging instance.protected booleanProtected for I2Ping since 0.9.11.(package private) static final intLegacy cap on consecutive connect timeouts before abandoning tunnel failover.(package private) static final longMinimum remaining budget for starting one connect leg.List of active sockets.(package private) static final longDeadline sentinel for callers that have not adopted the shared request budget (SOCKS, IRC, DCC, rawcreateI2PSocket): they keep their original per-call limits because this sentinel never reads as expired and never clamps.(package private) static final longOne-shot deadline extension granted while the outbound tunnel pool is still building (poolState() == 0) when the shared budget runs out.static final StringProperty name: max concurrently active client connections handled by one client tunnel.static final StringProperty turning on SSL between the proxy and the client.(package private) static final longSingle wall-clock budget for establishing one proxied request.protected final ObjectGuards sockMgr and mySockets.protected I2PSocketManagerThe socket manager.protected ServerSocketServer socket.Constructor Summary
ConstructorsConstructorDescriptionI2PTunnelHTTPClient(int localPort, Logging l, boolean ownDest, String wwwProxy, EventDispatcher notifyThis, I2PTunnel tunnel) Create an HTTP proxy client on the given local port.I2PTunnelHTTPClient(int localPort, Logging l, I2PSocketManager sockMgr, I2PTunnel tunnel, EventDispatcher notifyThis, long clientId) This constructor always starts the tunnel (ignoring thei2cp.delayOpenoption).Method Summary
Modifier and TypeMethodDescription(package private) static voidRetire a request's warm slot: closes an installed socket, releases the permit the slot still holds, and rejects an attempt still in flight (its thread releases the lease when the install is refused).private static URIChange various parts of the URI.protected voidNote: This does not handle RFC 2616 header line splitting, which is obsoleted in RFC 7230.booleanclose(boolean forced) Overridden to close internal socket too.private static voidcloseQuietly(I2PSocket sock) Closes a socket, ignoring any error.private voidexecuteTask(Thread task) Execute a runnable task inline.(package private) static URIFix up a URI whose path or query holds characters that RFC 3986 reserves but that browsers send unescaped: '[', ']', '|', '{' and '}'.(package private) static longgetConnectRetryDelayMs(int attempt) Exponential backoff delay (in ms) before the given connect retry.protected I2PSocketOptionsCreate the default options (using the default timeout, etc).protected I2PSocketOptionsgetDefaultOptions(Properties overrides) Create the default options (using the default timeout, etc).private StringgetHostName(String host) The hostname of the given host.protected StringgetRealm()Authentication realm for the proxy.(package private) static booleanisBrowserPeerClosed(Socket s, I2PTunnelHTTPClient.InputReader reader, String method) Probe whether the browser peer has closed while we wait on I2P connect.private static booleanisSupportedAddress(String protocol) Checks whether the tunnel type serves this protocol.private DestinationlookupWithTimeout(String hostname, long timeoutMs) Look up a hostname with a timeout to prevent blocking the request handler.private I2PSocketopenEmptyReconnect(Destination dest, int port, int emptyBudget, long requestId, boolean quiet) Open a single fresh I2P socket for an empty-response / body-resume reconnect, with the standard attempt budget and dead-pool fail-fast.private voidopenSpeculativeWarm(Destination dest, int port, int emptyBudget, long requestId, I2PTunnelHTTPClient.WarmSlot slot) Open one secondary I2P socket in the background while the primary is healthy.(package private) static intparseEmptyRetries(String value) Parse the empty-response retry count from a client option value.private static voidreleaseConnPermit(Hash dest) Release a concurrent-connection permit fordest.private static String[]removeHelper(String query) Remove the address helper from an encoded query.private static URIreplaceQuery(URI uri, String query) Replace query in the URI.(package private) static intscaledEmptyReconnectBudget(int baseBudget, long contentLength) Scale the reconnect attempt budget by the entity size already verified from this exchange's Content-Length header.(package private) static booleanshouldStopEmptyReconnect(int poolState) Whether the empty-response reconnect loop should stop instead of trying again.(package private) static booleansleepQuietly(long delayMs) Sleep for the given delay, restoring the interrupt flag if interrupted.voidActually start working on incoming connections.private static booleantryAcquireConnPermit(Hash dest) Try to acquire a concurrent-connection permit fordest.private voidwriteB32SaveForm(OutputStream outs, String destination, int code, String targetRequest) Write the HTML form that prompts the user to supply decryption/authentication data.private voidwriteErrorPage(OutputStream out, I2PTunnelHTTPClient.InputReader reader, String page, String body) Write an error page and drain any remaining request data.private voidwriteHelperSaveForm(OutputStream outs, String destination, String ahelperKey, String targetRequest, String referer) Write the HTML form that prompts the user to save an address helper destination.(package private) static voidwriteServiceUnavailable(OutputStream out, String message) Write a minimal 503 response and close the connection.Methods inherited from class I2PTunnelHTTPClientBase
_t, _t, _t, addrAndPort, authorize, buildShed503Response, buildShedRefreshResponse, decodeIDNHost, getAuthError, getErrorPage, getErrorPage, getPrefix, handleClientException, handleI2PSocketException, isBlockedIPv4Address, isBlockedLocalAddress, isDigestAuthRequired, noteProxyResult, optionsUpdated, parseShedAttempt, sanitizeAuthArgs, sanitizeLogValue, selectProxy, selectSSLProxy, shouldMetaRefresh, summarizeAuthorization, writeErrorMessage, writeErrorMessage, writeErrorMessage, writeErrorMessage, writeFooter, writeFooter, writeShedResponseModifier and TypeMethodDescriptionprotected StringLook up a translation key in this class's resource bundle.protected StringTranslate with one parameter {0}.protected StringTranslate with two parameters {0} and {1}.(package private) static StringFormat the remote address as IP:PORT for ban and failure logging.protected I2PTunnelHTTPClientBase.AuthResultAuthorization Ref: RFC 2617 If the socket is an InternalSocket, no auth required.(package private) static StringFinal shed response after refresh budget is exhausted (or non-HTML).(package private) static StringbuildShedRefreshResponse(int nextAttempt) Intermediate shed page: unquoted meta refresh (no nested quotes in the tag) plus a Set-Cookie bump so the browser retries at mostI2PTunnelHTTPClientBase.SHED_MAX_REFRESHtimes before the final 503.static StringdecodeIDNHost(String host) Decode a hostname for display.protected StringgetAuthError(boolean isStale) What to send if digest auth fails.protected StringgetErrorPage(String base, String backup) Load the error page header file, e.g.protected static StringgetErrorPage(I2PAppContext ctx, String base, String backup) Load the error page header file, e.g.protected StringgetPrefix(long requestId) Generates a unique log prefix for request tracking.protected voidhandleClientException(Exception ex, OutputStream out, String targetRequest, boolean usingWWWProxy, String wwwProxy, long requestId) Handle an exception from a client connection.protected voidhandleI2PSocketException(I2PSocketException ise, OutputStream out, String targetRequest, boolean usingWWWProxy, String wwwProxy) Generate an error page based on the status code in our custom exception.static booleanisBlockedIPv4Address(String host) Determine whether an IPv4 host is in a blocked non-routable block: loopback, "this network", RFC 1918 private, link-local, carrier-grade NAT, benchmarking, documentation, multicast, and reserved space.static booleanisBlockedLocalAddress(String host) Determine whether a proxy request target is a loopback, private, link-local, shared, or otherwise non-routable address that must not be reached through the proxy.protected booleanChecks if HTTP Digest authentication is required by the outproxy.protected voidnoteProxyResult(String proxy, String host, boolean isSSL, boolean ok) Update the cache and note if failed.voidoptionsUpdated(I2PTunnel tunnel) Update the outproxy list then call super.(package private) static intparseShedAttempt(String cookie) How many shed meta-refresh attempts the request's Cookie header already used.sanitizeAuthArgs(Map<String, String> args) Copy digest request arguments into a form safe to log: credential material is redacted, and every remaining value is stripped of line breaks and capped, so a hostile upstream cannot forge extra log lines or grow the log without bound.(package private) static StringsanitizeLogValue(String value) Strip line breaks and cap the length of an attacker-supplied value before it is written to the log, so a hostile username cannot forge extra log lines.protected StringselectProxy(String host) Selects an appropriate outproxy for the given host from the proxy pool.protected StringselectSSLProxy(String host) Selects an SSL-capable outproxy for HTTPS connections.(package private) static booleanshouldMetaRefresh(String method, String accept, int attempt) Whether a shed connection should get a meta-refresh page instead of a 503.(package private) static StringsummarizeAuthorization(String authorization) Summarize an Authorization header for logging without disclosing credential material.protected voidwriteErrorMessage(String errMessage, OutputStream out, String targetRequest, boolean usingWWWProxy, String wwwProxy) No jump servers or extra message.protected voidwriteErrorMessage(String errMessage, OutputStream out, String targetRequest, boolean usingWWWProxy, String wwwProxy, String jumpServers) No extra message.protected voidwriteErrorMessage(String errMessage, String extraMessage, OutputStream out, String targetRequest, boolean usingWWWProxy, String wwwProxy) No jump servers.protected voidwriteErrorMessage(String errMessage, String extraMessage, OutputStream outs, String targetRequest, boolean usingWWWProxy, String wwwProxy, String jumpServers) Write the complete error message to the output stream.static voidwriteFooter(OutputStream out) Write the closing HTML footer and flush it down the stream.static voidwriteFooter(Writer out) Write the closing HTML footer and flush it down the writer.protected voidWrite a real HTTP status on a shed connection so the browser never sees an empty response: HTML GET navigations get a cookie-capped meta-refresh page (up toI2PTunnelHTTPClientBase.SHED_MAX_REFRESHtimes), everything else gets a 503.Methods inherited from class I2PTunnelClientBase
buildSocketManager, buildSocketManager, buildSocketManager, buildSocketManager, clampToDeadlineMs, closeSocket, connectDeadlineFrom, createI2PSocket, createI2PSocket, createI2PSocket, createI2PSocket, destroy, extendDeadlineForBuildingPool, getEffectiveMaxConnections, getListenHost, getLocalPort, getSocketManager, getSocketManager, getSocketOpenTimeout, isConnectTimeout, isDeadlineExpired, legConnectTimeoutMs, manageConnection, poolIsDefinitivelyDown, poolState, resolveEffectiveMaxConnections, resolveMaxConnections, resolveRunnerCeiling, run, shouldContinueFailover, shouldContinueFailover, shouldOuterRetryConnect, shouldOuterRetryConnect, verifySocketManagerModifier and TypeMethodDescriptionprotected I2PSocketManagerFor NON-SHARED clients (ownDest = true).protected static I2PSocketManagerbuildSocketManager(I2PTunnel tunnel) Create a disconnected socket manager for the tunnel, with no private key file.protected static I2PSocketManagerbuildSocketManager(I2PTunnel tunnel, String pkf) Create a disconnected socket manager for the tunnel, loading the persistent client dest from pkf if it is non-null.protected static I2PSocketManagerbuildSocketManager(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.(package private) static longclampToDeadlineMs(long requestedMs, long deadlineMs, long nowMs) Clamp a requested timeout to the time left before a shared deadline.static voidClose a socket silently.(package private) static longconnectDeadlineFrom(long nowMs) Compute the shared connect deadline for a request starting now.createI2PSocket(Destination dest) Create a new I2PSocket towards to the specified destination, adding it to the list of connections actually managed by this tunnel.createI2PSocket(Destination dest, int port) Create a new I2PSocket towards to the specified destination, adding it to the list of connections actually managed by this tunnel.createI2PSocket(Destination dest, I2PSocketOptions opt) Create a new I2PSocket towards to the specified destination, adding it to the list of connections actually managed by this tunnel.(package private) I2PSocketcreateI2PSocket(Destination dest, I2PSocketOptions opt, long deadlineMs) Create a new I2PSocket bounded by a shared request deadline.booleandestroy()Note that the tunnel cannot be reopened after this by calling startRunning(), as it will destroy the underlying socket manager.(package private) static longextendDeadlineForBuildingPool(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.(package private) intThe effective concurrent-connection cap for this tunnel at the moment the accept loop admits a connection.protected final InetAddressGet the listen host address.final intGet the local port.Returns the I2PSocketManager for this task.protected static I2PSocketManagergetSocketManager(I2PTunnel tunnel, String pkf) This is ONLY for shared clients.longReturns the socket open timeout in ms; close() will proceed after this even with active sockets.(package private) static booleanFor timeout wording.(package private) static booleanisDeadlineExpired(long deadlineMs, long nowMs) Whether a shared connect deadline has passed.(package private) static longlegConnectTimeoutMs(long requestedMs, long deadlineMs, long nowMs) Compute the connect timeout to apply to one failover leg under a shared deadline.protected voidManage the connection just opened on the specified socket.protected booleanAnd none are being built, so further connect retries cannot succeed.protected intRouter-context only, best-effort check of the client outbound tunnel pool.(package private) static intresolveEffectiveMaxConnections(boolean customized, int ownCap, int globalDefault) Pure decision helper forI2PTunnelClientBase.getEffectiveMaxConnections(): pick the per-tunnel override when one was explicitly configured, otherwise the Tuner-managed default cap.(package private) static intresolveMaxConnections(String configured) Resolve the max-concurrent-connections cap from the tunnel's configured value.(package private) static intresolveRunnerCeiling(boolean customized, int effectiveMax) Ceiling to pin on this tunnel's runner pool at creation time.voidrun()Non-final since 0.9.11.(package private) static booleanshouldContinueFailover(int tunnelCount, int attemptsMade, int timeoutFailures, boolean poolDown) Whether another tunnel-failover attempt is worthwhile after a connect failure.(package private) static booleanshouldContinueFailover(int tunnelCount, int attemptsMade, int timeoutFailures, boolean poolDown, boolean poolBuilding) Whether another tunnel-failover attempt is worthwhile after a connect failure.(package private) static booleanshouldOuterRetryConnect(int connectAttempts, int timeoutConnectAttempts, boolean timedOut, boolean poolDown, boolean poolBuilding, boolean browserClosed) Whether the outer HTTP connect loop should retry after a failure.(package private) static booleanshouldOuterRetryConnect(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.protected voidCreate the manager if it doesn't exist, AND connect it to the router and build tunnels.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
addressHelpers
Map of host name to base64 destination for destinations collected via address helper links._pageNonce
Used to protect actions via http://proxy.i2p/AUTH_REALM
HTTP authentication realm for HTTP proxy.- See Also:
UA_I2P
User-Agent header sent to I2P sites.- See Also:
UA_CLEARNET
User-Agent header sent to clearnet sites.- See Also:
PROP_UA_I2P
Config key overriding the I2P user agent.- See Also:
PROP_UA_CLEARNET
Config key overriding the clearnet user agent.- See Also:
OPT_KEEPALIVE_BROWSER
Config key to enable browser-side keepalive.- See Also:
OPT_KEEPALIVE_I2P
Config key to enable I2P-side keepalive.- See Also:
OPT_EMPTY_RETRIES
Config key: max reconnects on a zero-byte (empty) upstream response for GET/HEAD.- See Also:
BROWSER_KEEPALIVE_TIMEOUT
static final int BROWSER_KEEPALIVE_TIMEOUTHow long to wait for another request on the same socket.- See Also:
DEFAULT_KEEPALIVE_BROWSER
private static final boolean DEFAULT_KEEPALIVE_BROWSERDefault keep-alive setting for browser connections.- See Also:
I2P_CONNECT_MAX_RETRIES
static final int I2P_CONNECT_MAX_RETRIESTotal connect attempts before the client stops retrying and fails the request.- See Also:
I2P_CONNECT_RETRY_BASE_DELAY
static final long I2P_CONNECT_RETRY_BASE_DELAYBackoff floor and per-attempt multiply base for I2P connect retries.- See Also:
DEFAULT_KEEPALIVE_I2P
private static final boolean DEFAULT_KEEPALIVE_I2PDefault keep-alive setting for I2P connections.- See Also:
DEFAULT_EMPTY_RETRIES
private static final int DEFAULT_EMPTY_RETRIESDefault reconnects on an empty upstream response before giving up.- See Also:
NAMING_IN_SESSION_TIMEOUT_MS
static final long NAMING_IN_SESSION_TIMEOUT_MSIn-session b32/b33 naming lookup budget, clamped to the shared request connect deadline so naming cannot outlive the request.- See Also:
NAMING_SERVICE_TIMEOUT_MS
static final long NAMING_SERVICE_TIMEOUT_MSNaming-service lookup budget for out-of-session and hostname lookups, clamped to the shared request connect deadline.- See Also:
MAX_CONNS_PER_DEST
private static final int MAX_CONNS_PER_DESTPer-destination concurrent outbound connection limit. Caps the number of simultaneous I2P sockets the HTTP proxy opens to the same destination. Without any gate, a misbehaving client page-load can fire many parallel requests that each independently callcreateI2PSocket(), creating a SYN storm to the remote server (20+ simultaneous SYNs observed). The server's inbound SYN-burst gate (ConnectionManager.checkSynBurst) records a strike for exceedingtempBanSynBurstSYNs intempBanSynRatems and autobans on a second strike within 60 seconds.The gate is pure insurance: browsers already self-limit parallel connections per host (typically 6 over HTTP/1.1, fewer under HTTP/2), and keep-alive means steady-state concurrency per destination is low. The cap is therefore set high enough that a legitimate page-load burst can never hit it -- being tied to the same destination means kicking a 5th+ parallel request would make a browser asset silently fail -- while still bounding an out-of-control client (hundreds of connections) far below the network-path burst that trips the remote SYN-burst gate.
A permit is acquired before
createI2PSocket()and released when the I2P socket is closed (after the tunnel-runner completes). With I2P keepalive the socket is reopened per request, so the permit lifecycle matches the actual connection lifetime. A dual-race secondary socket holds an extra permit only until the race settles.- See Also:
_activeConns
Active outbound I2P socket count per destination hash.AtomicIntegervalues; incremented beforecreateI2PSocket(), decremented when the socket closes._raceBudget
Per-remote-dest race token bucket for empty-response dual-race opens. Values arelong[2]={tokensMilli, lastRefillMs}, shared across all parallel requests to the same dest so the SYN budget is a property of the remote (one source dest from their view), not of a single browser request. SeeI2PTunnelRunner.tryConsumeRaceBudget(long[], long, int).ERR_REQUEST_DENIED
These are backups if the xxx.ht error page is missing.- See Also:
ERR_BLACKLISTED
Error response for a blacklisted address.- See Also:
ERR_NO_OUTPROXY
Error response when no outproxy is configured.- See Also:
ERR_AHELPER_CONFLICT
Error response for an address helper key conflict.- See Also:
ERR_AHELPER_NOTFOUND
Error response for an unresolvable helper key.- See Also:
ERR_AHELPER_NEW
Error response for a new address helper host.- See Also:
ERR_BAD_PROTOCOL
Error response for a non-HTTP request protocol.- See Also:
ERR_BAD_URI
Error response for an invalid request URI.- See Also:
ERR_LOCALHOST
Error response for a localhost request through the proxy.- See Also:
ERR_INTERNAL_SSL
Error response for SSL requested to an I2P address.- See Also:
isr
Socket runner for private mode, null otherwise.HELPER_PARAM
URL parameter carrying an address helper.- See Also:
LOCAL_SERVER
Local HTTP proxy server hostname.- See Also:
DEFAULT_GZIP
private static final boolean DEFAULT_GZIPDefault setting for gzipping responses.- See Also:
PROP_REFERER
PROP_USER_AGENT
Config key to enable sending User-Agent header.- See Also:
PROP_VIA
Config key to enable sending Via header.- See Also:
PROP_JUMP_SERVERS
Config key for jump server list.- See Also:
PROP_DISABLE_HELPER
Config key to disable address helper.- See Also:
PROP_ACCEPT
Config key: false, the default, replaces the browser's Accept header with a generic value for the content types it names; true forwards it unchanged.- See Also:
PROP_INTERNAL_SSL
Config key: false denies SSL to I2P destinations, but only once PROP_SSL_SET shows the user configured SSL by hand. Defaults to true (allow) either way.- See Also:
PROP_SSL_SET
Config key: set when the user configures SSL by hand. Its presence is the gate that lets PROP_INTERNAL_SSL deny internal SSL.- See Also:
DEFAULT_JUMP_SERVERS
Default jump server URLs.- See Also:
ERR_HELPER_DISABLED
Error response when address helpers are disabled.- See Also:
Constructor Details
I2PTunnelHTTPClient
public I2PTunnelHTTPClient(int localPort, Logging l, I2PSocketManager sockMgr, I2PTunnel tunnel, EventDispatcher notifyThis, long clientId) This constructor always starts the tunnel (ignoring thei2cp.delayOpenoption). It is used to add a client to an existing socket manager.As of 0.9.20 this is fast, and does NOT connect the manager to the router, or open the local socket. You MUST call
startRunning()for that.- Parameters:
localPort- the local port to bind tol- the logger inherited from the parent tunnelsockMgr- the existing socket managertunnel- the parent I2PTunnel instancenotifyThis- event dispatcher for notificationsclientId- the client identifier
I2PTunnelHTTPClient
public I2PTunnelHTTPClient(int localPort, Logging l, boolean ownDest, String wwwProxy, EventDispatcher notifyThis, I2PTunnel tunnel) throws IllegalArgumentException Create an HTTP proxy client on the given local port.Since 0.9.20 this is fast, and does NOT connect the manager to the router, or open the local socket. You MUST call startRunning() for that.
- Parameters:
localPort- the local port to bind tol- the logger inherited from the parent tunnelownDest- whether to use our own destinationwwwProxy- comma-separated list of outproxy URLs to forward through, or null for no outproxyingnotifyThis- event dispatcher for notificationstunnel- the parent I2PTunnel instance- Throws:
IllegalArgumentException- if the I2PTunnel does not contain valid config to contact the router
Method Details
tryAcquireConnPermit
Try to acquire a concurrent-connection permit fordest. The acquire is a per-key atomic remap on_activeConns: on rejection the map value is left untouched at the limit (never a sentinel), so the count can never be corrupted by writes of -1/0 and over-admission is impossible even under heavy parallel load.- Parameters:
dest- the destination to take a permit for, null always acquires- Returns:
- true if the permit was acquired, false if the destination is at its connection limit
releaseConnPermit
Release a concurrent-connection permit fordest. Per-key atomic remap: the entry is removed from_activeConnsexactly when the count reaches zero, so the map cannot grow without bound over the lifetime of the process. Safe to call with null or when the count is already zero (underflow-guarded).- Parameters:
dest- the destination to release one permit for, null does nothing
executeTask
Execute a runnable task inline.- Parameters:
task- Thread task to execute
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.- Overrides:
getDefaultOptionsin classI2PTunnelClientBase- Returns:
- the default options
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. Do not use overrides for per-socket options.- Overrides:
getDefaultOptionsin classI2PTunnelClientBase- Parameters:
overrides- the properties to override- Returns:
- the default options
startRunning
public void startRunning()Actually start working on incoming connections. Overridden to start an internal socket too.- Overrides:
startRunningin classI2PTunnelClientBase
close
public boolean close(boolean forced) Overridden to close internal socket too.- Overrides:
closein classI2PTunnelClientBase- 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
getRealm
Authentication realm for the proxy.- Specified by:
getRealmin classI2PTunnelHTTPClientBase- Returns:
- realm string
writeErrorPage
private void writeErrorPage(OutputStream out, I2PTunnelHTTPClient.InputReader reader, String page, String body) Write an error page and drain any remaining request data.- Parameters:
out- the output streamreader- reads the browser's request datapage- the error page typebody- the error body
clientConnectionRun
Note: This does not handle RFC 2616 header line splitting, which is obsoleted in RFC 7230.- Specified by:
clientConnectionRunin classI2PTunnelClientBase- Parameters:
s- the socket to manage
isBrowserPeerClosed
Probe whether the browser peer has closed while we wait on I2P connect. Only meaningful for GET/HEAD (no request body still unread). A timed read returns -1 on FIN (peer closed), times out when still open, or yields a pipelined byte which is pushed back so it is not lost.- Parameters:
s- the browser-facing socket, which may be nullreader- the request reader wrapping a PushbackInputStreammethod- HTTP method, or null- Returns:
- true if the peer has closed (or the socket is unusable)
cancelWarmSlot
Retire a request's warm slot: closes an installed socket, releases the permit the slot still holds, and rejects an attempt still in flight (its thread releases the lease when the install is refused). Idempotent and null-safe. Called from per-iteration cleanup and the request's finally block, where either may already have run.- Parameters:
slot- the warm slot to retire, or null
openSpeculativeWarm
private void openSpeculativeWarm(Destination dest, int port, int emptyBudget, long requestId, I2PTunnelHTTPClient.WarmSlot slot) Open one secondary I2P socket in the background while the primary is healthy. Fail-open: if the slot is not idle, the per-dest permit is unavailable, or the connect fails, no warm socket is installed and resume falls back to a cold open. Skipped whenI2PTunnelClientBase.poolIsDefinitivelyDown()so a dead outbound pool does not accumulate speculative connect storms. The attempt is tagged with the slot's generation, so a completion that races teardown is refused and its permit released by this thread instead of leaking.- Parameters:
dest- the destination to warm; its hash keys the per-dest connection permitport- remote port (0 = default)emptyBudget- connect attempt budget for the secondaryrequestId- logging prefix idslot- request-scoped warm slot, or null
openEmptyReconnect
private I2PSocket openEmptyReconnect(Destination dest, int port, int emptyBudget, long requestId, boolean quiet) Open a single fresh I2P socket for an empty-response / body-resume reconnect, with the standard attempt budget and dead-pool fail-fast. Shared by thereconnect()andreconnectPair()legs of the callback so both paths get identical backoff and stop conditions.- Parameters:
dest- the destination to open the fresh socket toport- remote port (0 = default)emptyBudget- max connect attempts for this requestrequestId- logging prefix idquiet- when true, suppress per-attempt Info logs (used for the staggered first race leg so the second leg's log is the pair marker)- Returns:
- a connected socket, or null if the budget / dead pool stopped us
closeQuietly
Closes a socket, ignoring any error.- Parameters:
sock- the socket to close, may be null
sleepQuietly
static boolean sleepQuietly(long delayMs) Sleep for the given delay, restoring the interrupt flag if interrupted.- Parameters:
delayMs- the delay in ms- Returns:
- false if the current thread was interrupted during the sleep
parseEmptyRetries
Parse the empty-response retry count from a client option value.Clamps to
>= 0; malformed or negative input falls back to 0. Pure decision — no context access, safe for unit tests.- Parameters:
value- the raw property value, may be null- Returns:
- the retry count: 0 (never retry) on null, empty, or unparseable input
scaledEmptyReconnectBudget
static int scaledEmptyReconnectBudget(int baseBudget, long contentLength) Scale the reconnect attempt budget by the entity size already verified from this exchange's Content-Length header.Body-resume reconnects run after response headers have arrived, so the size is known and verifiable; the budget gains one attempt per
I2PTunnelRunner.RETRY_RAMP_UNIT_BYTESof entity, capped at 9x the configured budget so a large file cannot pin the runner across an unbounded connect-retry marathon. The 9x cap saturates at the same entity size as the original 3x cap at 4MB granularity (8MB per base attempt). Unknown length (empty-response retry with no headers, or a sub-1MB entity) returns the base budget unchanged. Pure decision — no context access, safe for unit tests.- Parameters:
baseBudget- configured attempt budget for this requestcontentLength- entity Content-Length in bytes, or -1 if unknown- Returns:
- the scaled attempt budget; never below 0
getConnectRetryDelayMs
static long getConnectRetryDelayMs(int attempt) Exponential backoff delay (in ms) before the given connect retry. The first retry waitsI2P_CONNECT_RETRY_BASE_DELAY(1s), doubling per attempt up to a hard cap of 8s. During a tunnel-pool stall the capped backoff gives the pool time to recover instead of hammering it every 2s, and it still fails fast onceI2PTunnelClientBase.poolIsDefinitivelyDown()trips. Pure decision — no context access, safe for unit tests.- Parameters:
attempt- the 1-based connect failure count (how many failures so far)- Returns:
- the delay in ms: 0 for attempt <= 0, else 1000 << (attempt-1) bounded to 8000
shouldStopEmptyReconnect
static boolean shouldStopEmptyReconnect(int poolState) Whether the empty-response reconnect loop should stop instead of trying again. A connect attempt that just failed should not be re-driven when the client outbound pool is provably dead (poolState == -1): no further attempt can succeed, so retrying only stalls the browser for the full backoff budget (~39s at the default 9 attempts) and adds load to a mesh already failing to build replacement tunnels. Budget exhaustion is handled separately by the loop's exit condition, so this method only answers the "should we bail early on a dead pool" question.A
poolState == -2(unknown: standalone client, or the reflective check failed) deliberately does NOT fail fast — there is no live router pool to observe, so the budget is the only sane bound. Mirroring the initial-connect guard's<= -1test here would make standalone clients (outproxy, no router) abort every empty-response retry because they always read-2.- Parameters:
poolState- theI2PTunnelClientBase.poolState()value from the last failed attempt- Returns:
- true to stop retrying (fail to the normal empty-response error path)
writeHelperSaveForm
private void writeHelperSaveForm(OutputStream outs, String destination, String ahelperKey, String targetRequest, String referer) throws IOException Write the HTML form that prompts the user to save an address helper destination.- Parameters:
outs- the stream to write the page to, does nothing if nulldestination- the hostname echoed into the hidden form field, the page text, and the IDN-decoded lookupahelperKey- the base64 destination from the naming service, shown and embedded in the save formstargetRequest- the original request URL, the action of the continue-without-saving formreferer- the referring URL for a hidden field, null if none was sent- Throws:
IOException- if writing the page to the output stream fails
writeB32SaveForm
private void writeB32SaveForm(OutputStream outs, String destination, int code, String targetRequest) throws IOException Write the HTML form that prompts the user to supply decryption/authentication data.- Parameters:
outs- the stream to write the page to, does nothing if nulldestination- the base32 address whose lookup needs more datacode- the lookup result code naming what is requiredtargetRequest- the original request URL, the action of the supplied form- Throws:
IOException- if writing the page to the output stream fails
getHostName
isSupportedAddress
Checks whether the tunnel type serves this protocol.- Parameters:
protocol- the request protocol- Returns:
- true if the address type is supported
fixupURI
Fix up a URI whose path or query holds characters that RFC 3986 reserves but that browsers send unescaped: '[', ']', '|', '{' and '}'.The scheme, host and port are left untouched, so a fixup cannot move the request to a different host.
- Parameters:
request- the raw URI string from the request line- Returns:
- the URI, percent-escaped if a fixup was required
- Throws:
URISyntaxException- if the URI is invalid even after fixup
changeURI
Change various parts of the URI. String parameters are all non-encoded.Scheme always preserved. Userinfo always cleared. Host changed if non-null. Port changed if non-zero. Path changed if non-null. Query always preserved. Fragment always cleared.
- Parameters:
uri- the URI to rebuildhost- the new host, null keeps the originalport- the new port, 0 keeps the originalpath- the new path, null keeps the original- Returns:
- the rebuilt URI
- Throws:
URISyntaxException- if the replaced parts do not form a valid URI
replaceQuery
Replace query in the URI. Userinfo cleared if uri contained a query. Fragment cleared if uri contained a query.- Parameters:
uri- the URI whose query is replacedquery- an ENCODED query, removed if null- Returns:
- the URI carrying the new query, the original if it had none and none is given
- Throws:
URISyntaxException- if the URI assembled from the parts is not valid
removeHelper
Remove the address helper from an encoded query.- Parameters:
query- an ENCODED query, removed if null- Returns:
- rv[0] is ENCODED query with helper removed, non-null but possibly empty; rv[1] is DECODED helper value, non-null but possibly empty; rv null if no helper present
lookupWithTimeout
Look up a hostname with a timeout to prevent blocking the request handler. Naming service lookups can block on NetDB searches or network fetches for minutes if the destination isn't cached locally.- Parameters:
hostname- the name passed to the naming service; may be an IDN or b32 addresstimeoutMs- maximum time to wait- Returns:
- resolved Destination or null if lookup failed or timed out