Interface TcpInterceptor

Functional Interface:
This is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference.

@FunctionalInterface public interface TcpInterceptor
Sees, rewrites, drops and adds the bytes of raw TCP tunnels: mitmproxy's tcp_start, tcp_message, tcp_end and tcp_error hooks. Configure one per proxy with HttpProxyServerBootstrap.withTcpInterceptor(TcpInterceptor).

 bootstrap.withTcpInterceptor((message, ctx) -> message.fromClient()
         ? message.data()                                              // pass on
         : message.text().replace("ESMTP", "ESMTP proxied").getBytes(UTF_8));
 

Which tunnels

Every connection the proxy relays as bytes rather than as HTTP: a CONNECT tunnel that is not intercepted (no MITM manager, an ignored host, a declined ClientHello, a protocol other than TLS or HTTP, a server that speaks first), on an HTTP/1 connection or as a stream of an HTTP/2 one, and whether the server is reached directly, through a chained proxy, on a stream of an HTTP/2 connection or through an intercepted HTTPS proxy; reverse:tcp:// connections; and intercepted TLS sessions that do not carry HTTP (see HttpProxyServerBootstrap.withTcpInterceptor(org.microproxy.tcp.TcpInterceptor)), whose bytes the hooks see decrypted (TcpContext.tlsIntercepted()). Hosts matched by HttpProxyServerBootstrap.withTcpHosts(java.lang.String...) are tunnels even when they speak HTTP. Upgraded WebSocket connections are not: their frames go to the WebSocket frame hooks.

Messages

TCP has no message boundaries: a TcpMessage is whatever one read returned, so a peer's write may be seen in pieces or joined with the next one (see TcpMessage).

Each direction is read by a thread of its own, so the hooks of one tunnel are called from two threads at once, and those of many tunnels concurrently: the interceptor must be thread-safe. Within a direction, calls come in order, and the direction waits for each one: a slow hook slows that direction down.

Every tunnel gets tcpStart(org.microproxy.tcp.TcpContext) first, then its messages and half-closes, then either tcpEnd(org.microproxy.tcp.TcpContext) or tcpError(org.microproxy.tcp.TcpContext, java.lang.Throwable), never both. A hook that throws is logged; a throwing tcpMessage(org.microproxy.tcp.TcpMessage, org.microproxy.tcp.TcpContext) passes the message on unchanged.

Without an interceptor, tunnels copy bytes through pooled buffers as they always did, and cost nothing extra.

  • Method Summary

    Modifier and Type
    Method
    Description
    default void
    tcpEnd(TcpContext context)
    The tunnel ended: both directions finished, or one side closed the connection.
    default void
    tcpError(TcpContext context, Throwable error)
    The tunnel failed: a read or write failed (a reset, a timeout), and the connection was closed.
    default void
    tcpHalfClose(TcpContext context, boolean fromClient)
    One side finished sending (end of stream, a TCP half-close): called before the end is passed on to the other side, so TcpContext.send(boolean, byte[]) with the same fromClient can still add bytes in that direction.
    byte[]
    tcpMessage(TcpMessage message, TcpContext context)
    Bytes read from one side, before they are passed on.
    default void
    A tunnel opened: both sides are connected, nothing has been relayed yet (except the bytes the proxy read to decide what the connection is, which follow as the first message).
  • Method Details

    • tcpStart

      default void tcpStart(TcpContext context)
      A tunnel opened: both sides are connected, nothing has been relayed yet (except the bytes the proxy read to decide what the connection is, which follow as the first message). TcpContext.send(boolean, byte[]) may already add bytes, such as a greeting.
      Parameters:
      context - the tunnel
    • tcpMessage

      byte[] tcpMessage(TcpMessage message, TcpContext context)
      Bytes read from one side, before they are passed on.
      Parameters:
      message - the bytes, which side sent them and when
      context - the tunnel, and TcpContext.send(boolean, byte[]) to add bytes after these
      Returns:
      the bytes to pass on: message.data() as it is or changed in place, other bytes, or null or an empty array to drop them
    • tcpHalfClose

      default void tcpHalfClose(TcpContext context, boolean fromClient)
      One side finished sending (end of stream, a TCP half-close): called before the end is passed on to the other side, so TcpContext.send(boolean, byte[]) with the same fromClient can still add bytes in that direction. The other direction goes on.
      Parameters:
      context - the tunnel
      fromClient - whether the client finished sending, rather than the server
    • tcpEnd

      default void tcpEnd(TcpContext context)
      The tunnel ended: both directions finished, or one side closed the connection.
      Parameters:
      context - the tunnel
    • tcpError

      default void tcpError(TcpContext context, Throwable error)
      The tunnel failed: a read or write failed (a reset, a timeout), and the connection was closed. Called instead of tcpEnd(org.microproxy.tcp.TcpContext).
      Parameters:
      context - the tunnel
      error - what failed