java.lang.Object
net.i2p.i2ptunnel.I2PTunnelHTTPServer.Sender
All Implemented Interfaces:
Runnable
Enclosing class:
I2PTunnelHTTPServer

static class I2PTunnelHTTPServer.Senderextends Object implements Runnable
Pumps a client request body through to the I2P side and back.
  • Field Details

    • BUF_SIZE

      private static final int BUF_SIZE
      Copy buffer size for both directions.
      See Also:
    • _out

      private final OutputStream _out
      The output stream to the browser.
    • _in

      private final InputStream _in
      The input stream from the browser.
    • _name

      private final String _name
      Connection name used in the logs.
    • _targetLabel

      private volatile String _targetLabel
      Identity of the far side, e.g. [skank / aanoquc2]. The request direction used to log a bare "from Client -> Server" with no indication of which eepsite or client a transfer belonged to, which made slow small transfers impossible to attribute.
    • _requestLabel

      private volatile String _requestLabel
      Parsed request line for the transfer, set once known. The request direction builds the Sender before the request line is read, so this is applied after the fact and rendered only when present.
    • _log

      private final Log _log
      Logger, shadows the one in the superclass.
    • _failure

      private volatile IOException _failure
      The failure that ended the copy, if any.
    • _bytesTransferred

      private volatile long _bytesTransferred
      Total bytes transferred successfully (read from _in, written to _out).
    • _lastReadNanos

      private volatile long _lastReadNanos
      NanoTime of the last successful read from _in. Used for stall detection.
    • _lastWriteNanos

      private volatile long _lastWriteNanos
      NanoTime of the last completed write to _out. Tracked separately from the read stamp because the two stall for entirely different reasons: a stale read stamp means the local source is not delivering, while a fresh read with a stale write stamp means the destination is not accepting, i.e. I2P egress congestion. Both used to look identical, because the read stamp was only advanced after the write returned, so a write blocked on congestion was reported as a stalled source and pointed every diagnosis at the wrong layer.
  • Constructor Details

    • Sender

      public Sender(OutputStream out, InputStream in, String name, Log log)
      Create a Sender to copy data from input to output streams. Caller MUST close streams after Sender completes.
      Parameters:
      out - the output stream to write to
      in - the input stream to read from
      name - descriptive name for logging
      log - the logging instance
    • Sender

      public Sender(OutputStream out, InputStream in, String name, String targetLabel, Log log)
      Create a Sender with an explicit far-side label.
      Parameters:
      out - the output stream to write to
      in - the input stream to read from
      name - descriptive name for logging
      targetLabel - far-side identity, e.g. [skank / aanoquc2]; may be null or empty
      log - the logging instance
  • Method Details

    • setTargetLabel

      void setTargetLabel(String label)
      Set the far-side label. The request direction constructs the Sender before the request line is parsed, so the label is applied once it is known rather than at construction.
      Parameters:
      label - far-side identity, or null/empty to leave unset
    • setRequestLabel

      void setRequestLabel(String request)
      Set the parsed request line once it is known.
      Parameters:
      request - request line or path; null/empty/"Unknown request" leaves it unset
    • describeTarget

      static String describeTarget(String label, String request)
      Describe the far side for a log line: label plus optional request target, e.g. [skank / aanoquc2] GET /installers/i2pinstall.exe. Pure and static so the formatting is testable without streams.
      Parameters:
      label - far-side identity; may be null
      request - parsed request line or path; may be null
      Returns:
      the suffix to append to a transfer name, "" if neither is known
    • formatTransferRate

      static String formatTransferRate(long bytes, double secs)
      Format a transfer rate as 10.5K/s / 1.2M/s, in bytes per second using 1000-based units. Suffixes are B/s below 1 KB/s, K/s below 1 MB/s, and M/s above, so a rate never reads as a bare number with an ambiguous "kbps" that is easy to mistake for bits per second.
      Parameters:
      bytes - total bytes transferred, negative if the count is not trusted
      secs - elapsed seconds; a value of 0 or less formats as "0.0B/s"
      Returns:
      formatted rate, e.g. 60.6K/s; "0.0B/s" if secs <= 0
    • run

      public void run()
      Copy data from the input stream to the output stream, flushing after each write to push data through any intermediate buffers (e.g. GZIP or MessageOutputStream). Without explicit flushes, data can accumulate in GZIP's deflater buffer or the streaming layer's MessageOutputStream buffer, causing sawtooth throughput patterns and intermittent stalls on the receiving end.

      Tracks bytes transferred and detects stalls: if no data is read for I2PTunnelHTTPServer.ioStallTimeoutMs, the loop breaks and the partial transfer is logged. This prevents a dead peer from holding a handler or I/O thread indefinitely.

      Specified by:
      run in interface Runnable
    • getFailure

      public IOException getFailure()
      Get any IOException that occurred during the copy.
      Returns:
      the failure exception, or null if successful
    • getBytesTransferred

      public long getBytesTransferred()
      Total bytes successfully transferred (read from input, written to output).
      Returns:
      bytes transferred, updated live during the transfer
    • getName

      public String getName()
      The label this transfer is logged and reported under.
      Returns:
      the transfer description ("Server -> Client ..."); never null
    • getLastReadNanos

      public long getLastReadNanos()
      NanoTime of the last successful read. Used by callers to detect stalls without interrupting the Sender thread.
      Returns:
      System.nanoTime() of the last successful read
    • getLastWriteNanos

      public long getLastWriteNanos()
      NanoTime of the last completed write. Compared against getLastReadNanos() to tell a silent local source from a congested I2P destination.
      Returns:
      System.nanoTime() of the last completed write
    • abort

      public void abort(IOException cause)
      Abort this transfer without ever running it, closing both ends so the connection is torn down promptly.

      Used when the body cannot be handed to the I/O pool. Running the body on the caller's handler thread instead would pin that thread for the whole transfer, which is how one saturated body starves the runner pool and takes the entire service offline; a client that gets a fast close retries, while one that gets a pinned thread waits indefinitely.

      Parameters:
      cause - recorded as the transfer failure, propagated to the caller