All Implemented Interfaces:
Runnable, LimitOutputStream.DoneCallback

public class I2PTunnelHTTPClientRunnerextends I2PTunnelRunner
HTTP client runner that filters response headers to enforce the "Connection: close" header, ensuring browsers treat the connection as non-persistent, even if the server doesn't comply.

This class is internal and not part of a stable public API.

Implements proper resource cleanup, supports optional keep-alive for both I2P and browser sockets, and uses background threading for non-blocking socket closure when appropriate.

Thread Safety: This class is not thread-safe. It is expected to be used by a single connection thread.

  • Constructor Details

    • I2PTunnelHTTPClientRunner

      public I2PTunnelHTTPClientRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData, List<I2PSocket> sockList, I2PTunnelRunner.FailCallback onFail, boolean allowKeepAliveI2P, boolean allowKeepAliveSocket, boolean isHead)
      Constructs a new HTTP client runner.
      Parameters:
      s - local socket (browser side)
      i2ps - connected I2P socket
      slock - synchronization lock for shared resources
      initialI2PData - initial data to send over I2P
      sockList - list of active I2P sockets
      onFail - callback for failure events
      allowKeepAliveI2P - if true, optionally keep I2P socket alive after response
      allowKeepAliveSocket - if true, optionally keep browser socket alive after response
      isHead - true if responding to an HTTP HEAD request (no body expected)
      Throws:
      IllegalArgumentException - if keep-alive I2P is requested but browser socket keep-alive is disabled
  • Method Details

    • trackInitialResponseDeadline

      protected boolean trackInitialResponseDeadline()
      Whether this runner type arms the first-response deadline watchdog. False in the base runner so idle long-lived sessions (HTTP CONNECT, SOCKS, IRC, server-side tunnels) are never torn down for silence; the HTTP request/response runner overrides it to true. For CONNECT the remote proxy's 200 response would retire the watchdog anyway, but the default keeps the deadline strictly scoped to request/response flows.

      True here: an HTTP request/response strictly precedes its response, so a peer that never sends the first byte must be torn down by the initial-response deadline instead of hanging the runner forever (read timeouts are disabled for HTTP tunnels and the body stall timeout is only armed after the first byte).

      Overrides:
      trackInitialResponseDeadline in class I2PTunnelRunner
      Returns:
      true to schedule the initial-response watchdog
    • getSocketOut

      protected OutputStream getSocketOut() throws IOException
      Returns a filtered HTTP response OutputStream that enforces the "Connection: close" header. Should be called only once per instance.
      Overrides:
      getSocketOut in class I2PTunnelRunner
      Returns:
      an output stream wrapping the socket's output stream that filters HTTP headers
      Throws:
      IllegalStateException - if called more than once
      IOException - if an I/O error occurs
    • getKeepAliveSocket

      boolean getKeepAliveSocket()
      Should we keep the local browser socket open when done? Checks the HTTPResponseOutputStream's keep-alive decision based on actual response headers.
      Overrides:
      getKeepAliveSocket in class I2PTunnelRunner
      Returns:
      true if the local socket should remain open for reuse
    • getKeepAliveI2P

      boolean getKeepAliveI2P()
      Should we keep the I2P socket open when done? Checks the HTTPResponseOutputStream's keep-alive decision based on actual response headers.
      Overrides:
      getKeepAliveI2P in class I2PTunnelRunner
      Returns:
      true if the I2P socket should remain open for reuse
    • getBodyProgress

      protected I2PTunnelRunner.BodyProgress getBodyProgress()
      Body-delivery progress from the HTTP response stream, for Range resume.
      Overrides:
      getBodyProgress in class I2PTunnelRunner
      Returns:
      progress snapshot, or null if the response stream is not up yet
    • prepareBodyResume

      protected void prepareBodyResume()
      Put the response stream into mid-body resume mode (swallow next headers).
      Overrides:
      prepareBodyResume in class I2PTunnelRunner
    • wasTransientResumeFailure

      protected boolean wasTransientResumeFailure()
      Whether the last body-resume attempt failed with a transient status (408/502/503/504) rather than a real stall. Observed once, then cleared so the next attempt starts fresh.
      Overrides:
      wasTransientResumeFailure in class I2PTunnelRunner
      Returns:
      true if the last resume aborted with a transient status
    • hasTransientResumeFailure

      protected boolean hasTransientResumeFailure()
      Peek at the latched transient-failure flag without clearing it — used by the forwarder finally-block to keep the browser stream open while the non-Range fallback is still pending.
      Overrides:
      hasTransientResumeFailure in class I2PTunnelRunner
      Returns:
      true if a transient status aborted the last resume attempt
    • close

      protected void close(OutputStream out, InputStream in, OutputStream i2pout, InputStream i2pin, Socket s, I2PSocket i2ps, Thread t1, Thread t2) throws InterruptedException
      Closes all streams and sockets, respecting keep-alive settings. Optionally closes I2P socket in a background thread if keep-alive is enabled on the browser socket.
      Overrides:
      close in class I2PTunnelRunner
      Parameters:
      out - may be null
      in - may be null
      i2pout - may be null
      i2pin - may be null
      s - the local socket to close, non-null
      i2ps - the I2P socket to close, non-null
      t1 - may be null
      t2 - may be null, ignored, we only join t1
      Throws:
      InterruptedException - if the joining thread is interrupted while waiting on t1