Class FlowContext

java.lang.Object
org.microproxy.FlowContext
Direct Known Subclasses:
FullFlowContext

public class FlowContext extends Object
Information about the client side of a proxied flow.
  • Constructor Details

    • FlowContext

      public FlowContext(long connectionId, Supplier<InetSocketAddress> clientAddress, Supplier<SSLSession> clientSslSession, ClientDetails clientDetails)
      Creates a context for the supplied client address and TLS session.
      Parameters:
      connectionId - the client connection identifier
      clientAddress - the supplier of the current client socket address
      clientSslSession - the supplier of the client TLS session, which may return null
      clientDetails - the client address and authentication details
    • FlowContext

      protected FlowContext(FlowContext parent)
      Creates a context that shares identity and timing data with parent.
      Parameters:
      parent - the client connection context
    • FlowContext

      protected FlowContext(FlowContext connection, int streamId)
      Creates the context of one HTTP/2 stream of the client connection connection: it shares the connection's identity (id, addresses, TLS session, client details, acceptance time) and has its own exchange record and timing data, since a connection's streams run at the same time.
      Parameters:
      connection - the parent client connection context
      streamId - the stream's identifier, positive
    • FlowContext

      protected FlowContext(FlowContext connection, int streamId, HttpVersion version)
      Creates the context of one stream of the client connection connection, as FlowContext(FlowContext, int) does, for an HTTP/2 or an HTTP/3 connection. HTTP/3 streams are QUIC streams, whose ids start at 0.
      Parameters:
      connection - the parent client connection context
      streamId - the stream's identifier: positive for HTTP/2, 0 or more for HTTP/3
      version - HttpVersion.HTTP_2_0 or HttpVersion.HTTP_3_0
  • Method Details

    • getConnectionId

      public long getConnectionId()
      A process-unique id of the client connection. The proxy's log lines about the connection start with [conn <id>].
      Returns:
      a process-unique id of the client connection
    • getStreamId

      public int getStreamId()
      The HTTP/2 or HTTP/3 stream that carries this flow's exchange, or 0 when the client connection is HTTP/1 (whose exchanges run one after another). An HTTP/3 stream's id is its QUIC stream id, and the first one is 0 too: getHttpVersion() tells them apart. Streams of one connection run concurrently; the proxy's log lines about a stream start with [conn <id> stream <streamId>].
      Returns:
      the stream that carries this flow's exchange, or 0 for an HTTP/1 connection
    • getHttpVersion

      public HttpVersion getHttpVersion()
      The HTTP version of the client connection that carries this flow: HttpVersion.HTTP_2_0 or HttpVersion.HTTP_3_0 for a stream's flow, HttpVersion.HTTP_1_1 for an HTTP/1.x connection (whose request lines tell 1.0 from 1.1) and for the context of a connection itself. It is the client protocol whose ProtocolPolicy applies to the flow's exchanges (HttpProxyServer.getProtocolPolicy(org.microproxy.http.HttpVersion)), so filters can branch on it too.
      Returns:
      the client connection's HTTP version
    • getConnectionContext

      public FlowContext getConnectionContext()
      The context of the client connection this flow belongs to: this context itself (or the one it was made from) for HTTP/1, the connection's own for an HTTP/2 stream's. Use it for state kept per client connection; contexts of different streams are not equal.
      Returns:
      the context of the client connection this flow belongs to: this context itself (or the one it was made from) for HTTP/1, the connection's own for an HTTP/2 stream's
    • getClientAddress

      public InetSocketAddress getClientAddress()
      The client's address. When a PROXY protocol header was accepted, this is the original client address it carried rather than the address of the load balancer.
      Returns:
      the client's address
    • getDirectClientAddress

      public InetSocketAddress getDirectClientAddress()
      The peer of the client's TCP connection: the load balancer that sent a PROXY protocol header when one was accepted (HttpProxyServerBootstrap.withAcceptProxyProtocol(ProxyProtocolMode)), where getClientAddress() is the client that header named; otherwise the same as getClientAddress(). Access logs render it with %DOWNSTREAM_DIRECT_REMOTE_ADDRESS%.
      Returns:
      the address the client connection came from
    • getLocalAddress

      public InetSocketAddress getLocalAddress()
      The proxy's own address on the client connection: the local socket address of the listener's TCP connection the client made (the TLS, SOCKS, transparent and reverse proxy listeners' too), or, for HTTP/3 and transparent QUIC, the UDP address the QUIC connection is served on (which may be the listener's wildcard address). A connection redirected to the proxy (transparent mode) has the proxy's address here, not the one the client meant to reach. A request inside an intercepted CONNECT stream of an HTTP/2 or HTTP/3 connection, or inside a CONNECT-UDP flow, has that connection's. It is what the client connected to, as against getClientAddress() (the client, from the PROXY protocol header when one was accepted), getDirectClientAddress() (the TCP peer, a load balancer when it sent that header) and getProxyProtocolDestinationAddress() (the address that header says the client connected to, at the load balancer). Access logs render it with %DOWNSTREAM_LOCAL_ADDRESS%. null for contexts made outside the proxy.
      Returns:
      the address the client connected to, or null
    • getProxyProtocolDestinationAddress

      public InetSocketAddress getProxyProtocolDestinationAddress()
      The destination address of the PROXY protocol header the client connection started with (HttpProxyServerBootstrap.withAcceptProxyProtocol(ProxyProtocolMode)): the address the original client connected to, at the load balancer that sent the header, where getLocalAddress() is the proxy's own. Load balancers that serve several addresses or ports through one proxy listener tell them apart by it. null without a header, for a header without addresses (UNKNOWN, LOCAL), and for contexts made outside the proxy.
      Returns:
      the PROXY header's destination address, or null
    • getClientSslSession

      public SSLSession getClientSslSession()
      The TLS session with the client, or null. This is the proxy's own TLS listener session, or the intercepted session while a CONNECT is being man-in-the-middled.
      Returns:
      the TLS session with the client, or null
    • acceptedAt

      public Instant acceptedAt()
      When the client connection was accepted (when this context was created, for contexts made outside the proxy).
      Returns:
      when the client connection was accepted (when this context was created, for contexts made outside the proxy)
    • upstreamStatus

      public OptionalInt upstreamStatus()
      The status of the final response the server sent for the exchange in progress on this connection (or the last one), even when a filter or the cache changed or replaced it before it reached the client; empty when no server response was received (the proxy or a filter answered, or the server failed). Contexts made outside the proxy have none.
      Returns:
      the final upstream status, or empty when no upstream response was received
    • forwardedVia

      public Optional<String> forwardedVia()
      The session's server that the exchange in progress (or the last one) is forwarded through, as host:port, when the client takes that server for its proxy (an intercepted HTTPS proxy, or an HTTP proxy whose session is plain HTTP): the client sent a CONNECT or an absolute-form request inside the session, which the proxy forwards to that server rather than to the target itself, or the request travels inside the tunnel such a CONNECT opened. Empty otherwise (a filter that gives a request an absolute URI of its own sends it straight there), and for contexts made outside the proxy.
      Returns:
      the proxy's host:port, or empty
    • socksVersion

      public OptionalInt socksVersion()
      The SOCKS version the client reached the proxy with: 5, or 4 for SOCKS4 and 4a. The requests of a SOCKS client arrive as a CONNECT the proxy made up from its SOCKS request (see HttpProxyServerBootstrap.withSocksServer(boolean)), and as the requests inside the session that follows. Empty for HTTP clients, and for contexts made outside the proxy.
      Returns:
      the client's SOCKS version, or empty
    • isTransparentQuic

      public boolean isTransparentQuic()
      Whether the client's QUIC came to the transparent QUIC listener (HttpProxyServerBootstrap.withTransparentQuicPort(int)): redirected there, routed by the SNI of its ClientHello, and intercepted (its HTTP/3 requests) or relayed. False for every other client, and for contexts made outside the proxy.
      Returns:
      whether the flow is a transparent QUIC client's
    • isEarlyData

      public boolean isEarlyData()
      Whether the request of this HTTP/3 stream arrived, at least in part, as 0-RTT early data (with 0-RTT on). An attacker may have captured and replayed such a request (RFC 8446 section 8), so the proxy sends it on with Early-Data: 1 (RFC 8470), answers 425 Too Early to methods that are not safe to replay, and a filter may answer 425 to others. False for every other request, and for contexts made outside the proxy.
      Returns:
      whether the request came as early data
    • isChunkedWithContentLength

      public boolean isChunkedWithContentLength()
      Whether the HTTP/1.1 request of the exchange in progress (or the last one) arrived with both Transfer-Encoding: chunked and Content-Length, and was accepted because HttpProxyServerBootstrap.withAllowChunkedLength(boolean) is on: its Content-Length was removed before filters saw it, its body was read as chunked, and the client connection closes after the response (RFC 9112 section 6.1). Such requests are a request smuggling signal. False for every other request, and for contexts made outside the proxy.
      Returns:
      whether the request had both framings, and its Content-Length was dropped
    • failure

      public Optional<ProxyFailure> failure()
      Why the proxy had to answer the exchange in progress on this connection (or the last one) itself, or abandon it: the failure handed to HttpFilters.proxyToServerFailure(org.microproxy.ProxyFailure), set before the filters see it. Empty when there was none, and for contexts made outside the proxy. Read it in ActivityTracker.flowEnded(org.microproxy.FlowRecord) to see the whole exchange.
      Returns:
      the exchange's failure, or empty
    • responseFlags

      public EnumSet<ResponseFlag> responseFlags()
      Envoy-style flags saying why the exchange in progress on this connection (or the last one), or the tunnel or flow it opened, ended as it did (UF for a refused connection, DC for a client that left, ...). A copy, which may grow while the exchange goes on; empty for contexts made outside the proxy.
      Returns:
      a copy of the exchange's response flags
    • responseCodeDetails

      public Optional<String> responseCodeDetails()
      A short snake_case reason for the response of the exchange in progress on this connection (or the last one), as Envoy's %RESPONSE_CODE_DETAILS% gives: via_upstream for the server's response, filter, cache or replay for one made there, proxy_failure:connect_failed (and so on, one per ProxyFailure) for the proxy's answer to a failure, auth_rejected, too_early, downstream_remote_disconnect, ... Empty before the exchange has an outcome, and for contexts made outside the proxy.
      Returns:
      the exchange's response code details, or empty
    • responseSource

      public Optional<ResponseSource> responseSource()
      Where the response of the exchange in progress on this connection came from: inside HttpFilters.serverToProxyResponse(org.microproxy.http.HttpObject) and HttpFilters.proxyToClientResponse(org.microproxy.http.HttpObject), the source of the response the hook is handed (ResponseSource.SERVER for the server's, ResponseSource.PROXY for the proxy's own answers, such as its error responses and a CONNECT's 200, ResponseSource.FILTER for a short-circuit response or a HttpFilters.proxyToServerFailure(org.microproxy.ProxyFailure) answer, ResponseSource.CACHE and ResponseSource.REPLAY); once the response is sent, where the response the client got came from, as HttpFilters.proxyToClientResponseSent(org.microproxy.http.HttpResponse, org.microproxy.ResponseSource) and trackers are told. A filter that replaced or restatused the server's response in serverToProxyResponse makes it FILTER for proxyToClientResponse; within one hook of a HttpFiltersChain, a later filter is told what the proxy handed the chain. Empty before the exchange has a response, and for contexts made outside the proxy.
      Returns:
      the source of the exchange's response, or empty
    • addResponseFlag

      public void addResponseFlag(ResponseFlag flag)
      Adds flag to the response flags of the exchange in progress: for a filter that answers or refuses a request for a reason the access log should show (a rate limiter's ResponseFlag.RATE_LIMITED, say). Does nothing on a context made outside the proxy.
      Parameters:
      flag - the flag to add
    • setResponseCodeDetails

      public void setResponseCodeDetails(String details)
      Sets the response code details of the exchange in progress, for a filter that answers for a reason of its own: a short snake_case string, which replaces any details set before. Does nothing on a context made outside the proxy.
      Parameters:
      details - the details, such as concurrency_limited
    • trace

      public FlowTrace trace()
      The distributed trace of the exchange in progress on this connection (or the last one), made by the proxy's FlowTracer: its trace and span ids, and the span a filter may add attributes and events to. FlowTrace.NONE without a tracer, and for contexts made outside the proxy.
      Returns:
      the exchange's trace
    • timings

      public FlowTimings timings()
      When the phases of the exchange in progress on this connection (or the last one) happened: DNS lookup, connect, TLS handshakes, time to first byte, total. A snapshot; take it in ActivityTracker.responseCompleted(org.microproxy.FlowContext, org.microproxy.http.HttpResponse) (or ActivityTracker.flowEnded(org.microproxy.FlowRecord)) to see the whole exchange. Contexts made outside the proxy return FlowTimings.NONE.
      Returns:
      a snapshot of the exchange phase timings
    • getClientHello

      public ClientHello getClientHello()
      The TLS ClientHello the client sent to start the intercepted (or tunnelled) session this flow belongs to: after a CONNECT whose client was asked for one, and on a transparent TLS connection. null before then, for plain HTTP, for tunnels that were never inspected, and for contexts made outside the proxy.
      Returns:
      the client's ClientHello, or null
    • scriptStats

      public ScriptUsage scriptStats()
      What the calls into scripts for the exchange in progress on this connection (or the last one) added up to: how many, how long they took, the interpreter steps they ran and about how many bytes of values they created. Live totals, which grow while calls go on; ScriptUsage.NONE when no script was called for the exchange.
      Returns:
      the exchange's script totals
    • recordScriptCall

      public void recordScriptCall(long nanos, long steps, long allocBytes)
      Adds a call into a script to the exchange's totals; scripting modules call it at the end of every hook call they make for the exchange.
      Parameters:
      nanos - how long the call took
      steps - the interpreter steps it ran
      allocBytes - about how many bytes of values it created
    • resetScriptStats

      protected void resetScriptStats()
      Forgets the script totals of the previous exchange: for the proxy, when a new one starts.
    • getClientDetails

      public ClientDetails getClientDetails()
      Returns the client address and authentication details.
      Returns:
      the client address and authentication details
    • setTimingData

      public void setTimingData(String key, Long value)
      Stores a named legacy timing value.
      Parameters:
      key - the timing measurement name
      value - the timing value to record
    • getTimingData

      public Long getTimingData(String key)
      Returns a named legacy timing value.
      Parameters:
      key - the timing measurement name
      Returns:
      the recorded value, or null if absent
    • getTimings

      public Map<String,Long> getTimings()
      Returns the legacy timing values.
      Returns:
      an immutable snapshot of the legacy timing map
    • equals

      public boolean equals(Object o)
      Contexts are equal when they belong to the same client connection and the same HTTP/2 or HTTP/3 stream (none for HTTP/1), so maps keyed by context keep one entry per exchange in progress.
      Overrides:
      equals in class Object
    • hashCode

      public int hashCode()
      Overrides:
      hashCode in class Object