Package org.microproxy
Class FlowContext
java.lang.Object
org.microproxy.FlowContext
- Direct Known Subclasses:
FullFlowContext
Information about the client side of a proxied flow.
-
Constructor Summary
ConstructorsModifierConstructorDescriptionFlowContext(long connectionId, Supplier<InetSocketAddress> clientAddress, Supplier<SSLSession> clientSslSession, ClientDetails clientDetails) Creates a context for the supplied client address and TLS session.protectedFlowContext(FlowContext parent) Creates a context that shares identity and timing data withparent.protectedFlowContext(FlowContext connection, int streamId) Creates the context of one HTTP/2 stream of the client connectionconnection: 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.protectedFlowContext(FlowContext connection, int streamId, HttpVersion version) Creates the context of one stream of the client connectionconnection, asFlowContext(FlowContext, int)does, for an HTTP/2 or an HTTP/3 connection. -
Method Summary
Modifier and TypeMethodDescriptionWhen the client connection was accepted (when this context was created, for contexts made outside the proxy).voidaddResponseFlag(ResponseFlag flag) Addsflagto 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'sResponseFlag.RATE_LIMITED, say).booleanContexts 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.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 toHttpFilters.proxyToServerFailure(org.microproxy.ProxyFailure), set before the filters see it.The session's server that the exchange in progress (or the last one) is forwarded through, ashost: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 aCONNECTor 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 aCONNECTopened.The client's address.Returns the client address and authentication details.The TLSClientHellothe client sent to start the intercepted (or tunnelled) session this flow belongs to: after aCONNECTwhose client was asked for one, and on a transparent TLS connection.The TLS session with the client, ornull.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.longA process-unique id of the client connection.The peer of the client's TCP connection: the load balancer that sent a PROXY protocol header when one was accepted (HttpProxyServerBootstrap.withAcceptProxyProtocol(ProxyProtocolMode)), wheregetClientAddress()is the client that header named; otherwise the same asgetClientAddress().The HTTP version of the client connection that carries this flow:HttpVersion.HTTP_2_0orHttpVersion.HTTP_3_0for a stream's flow,HttpVersion.HTTP_1_1for an HTTP/1.x connection (whose request lines tell 1.0 from 1.1) and for the context of a connection itself.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).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, wheregetLocalAddress()is the proxy's own.intThe 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).getTimingData(String key) Returns a named legacy timing value.Returns the legacy timing values.inthashCode()booleanWhether the HTTP/1.1 request of the exchange in progress (or the last one) arrived with bothTransfer-Encoding: chunkedandContent-Length, and was accepted becauseHttpProxyServerBootstrap.withAllowChunkedLength(boolean)is on: itsContent-Lengthwas removed before filters saw it, its body was read as chunked, and the client connection closes after the response (RFC 9112 section 6.1).booleanWhether the request of this HTTP/3 stream arrived, at least in part, as 0-RTT early data (with0-RTTon).booleanWhether 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.voidrecordScriptCall(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.protected voidForgets the script totals of the previous exchange: for the proxy, when a new one starts.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_upstreamfor the server's response,filter,cacheorreplayfor one made there,proxy_failure:connect_failed(and so on, one perProxyFailure) for the proxy's answer to a failure,auth_rejected,too_early,downstream_remote_disconnect, ...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 (UFfor a refused connection,DCfor a client that left, ...).Where the response of the exchange in progress on this connection came from: insideHttpFilters.serverToProxyResponse(org.microproxy.http.HttpObject)andHttpFilters.proxyToClientResponse(org.microproxy.http.HttpObject), the source of the response the hook is handed (ResponseSource.SERVERfor the server's,ResponseSource.PROXYfor the proxy's own answers, such as its error responses and aCONNECT's200,ResponseSource.FILTERfor a short-circuit response or aHttpFilters.proxyToServerFailure(org.microproxy.ProxyFailure)answer,ResponseSource.CACHEandResponseSource.REPLAY); once the response is sent, where the response the client got came from, asHttpFilters.proxyToClientResponseSent(org.microproxy.http.HttpResponse, org.microproxy.ResponseSource)and trackers are told.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.voidsetResponseCodeDetails(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.voidsetTimingData(String key, Long value) Stores a named legacy timing value.The SOCKS version the client reached the proxy with: 5, or 4 for SOCKS4 and 4a.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.trace()The distributed trace of the exchange in progress on this connection (or the last one), made by the proxy'sFlowTracer: its trace and span ids, and the span a filter may add attributes and events to.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).
-
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 identifierclientAddress- the supplier of the current client socket addressclientSslSession- the supplier of the client TLS session, which may returnnullclientDetails- the client address and authentication details
-
FlowContext
Creates a context that shares identity and timing data withparent.- Parameters:
parent- the client connection context
-
FlowContext
Creates the context of one HTTP/2 stream of the client connectionconnection: 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 contextstreamId- the stream's identifier, positive
-
FlowContext
Creates the context of one stream of the client connectionconnection, asFlowContext(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 contextstreamId- the stream's identifier: positive for HTTP/2, 0 or more for HTTP/3version-HttpVersion.HTTP_2_0orHttpVersion.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
The HTTP version of the client connection that carries this flow:HttpVersion.HTTP_2_0orHttpVersion.HTTP_3_0for a stream's flow,HttpVersion.HTTP_1_1for 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 whoseProtocolPolicyapplies 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
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
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
The peer of the client's TCP connection: the load balancer that sent a PROXY protocol header when one was accepted (HttpProxyServerBootstrap.withAcceptProxyProtocol(ProxyProtocolMode)), wheregetClientAddress()is the client that header named; otherwise the same asgetClientAddress(). Access logs render it with%DOWNSTREAM_DIRECT_REMOTE_ADDRESS%.- Returns:
- the address the client connection came from
-
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 interceptedCONNECTstream 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 againstgetClientAddress()(the client, from the PROXY protocol header when one was accepted),getDirectClientAddress()(the TCP peer, a load balancer when it sent that header) andgetProxyProtocolDestinationAddress()(the address that header says the client connected to, at the load balancer). Access logs render it with%DOWNSTREAM_LOCAL_ADDRESS%.nullfor contexts made outside the proxy.- Returns:
- the address the client connected to, or
null
-
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, wheregetLocalAddress()is the proxy's own. Load balancers that serve several addresses or ports through one proxy listener tell them apart by it.nullwithout 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
The TLS session with the client, ornull. 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
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
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
The session's server that the exchange in progress (or the last one) is forwarded through, ashost: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 aCONNECTor 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 aCONNECTopened. 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
The SOCKS version the client reached the proxy with: 5, or 4 for SOCKS4 and 4a. The requests of a SOCKS client arrive as aCONNECTthe proxy made up from its SOCKS request (seeHttpProxyServerBootstrap.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 (with0-RTTon). An attacker may have captured and replayed such a request (RFC 8446 section 8), so the proxy sends it on withEarly-Data: 1(RFC 8470), answers425 Too Earlyto methods that are not safe to replay, and a filter may answer425to 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 bothTransfer-Encoding: chunkedandContent-Length, and was accepted becauseHttpProxyServerBootstrap.withAllowChunkedLength(boolean)is on: itsContent-Lengthwas 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-Lengthwas dropped
-
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 toHttpFilters.proxyToServerFailure(org.microproxy.ProxyFailure), set before the filters see it. Empty when there was none, and for contexts made outside the proxy. Read it inActivityTracker.flowEnded(org.microproxy.FlowRecord)to see the whole exchange.- Returns:
- the exchange's failure, or empty
-
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 (UFfor a refused connection,DCfor 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
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_upstreamfor the server's response,filter,cacheorreplayfor one made there,proxy_failure:connect_failed(and so on, one perProxyFailure) 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
Where the response of the exchange in progress on this connection came from: insideHttpFilters.serverToProxyResponse(org.microproxy.http.HttpObject)andHttpFilters.proxyToClientResponse(org.microproxy.http.HttpObject), the source of the response the hook is handed (ResponseSource.SERVERfor the server's,ResponseSource.PROXYfor the proxy's own answers, such as its error responses and aCONNECT's200,ResponseSource.FILTERfor a short-circuit response or aHttpFilters.proxyToServerFailure(org.microproxy.ProxyFailure)answer,ResponseSource.CACHEandResponseSource.REPLAY); once the response is sent, where the response the client got came from, asHttpFilters.proxyToClientResponseSent(org.microproxy.http.HttpResponse, org.microproxy.ResponseSource)and trackers are told. A filter that replaced or restatused the server's response inserverToProxyResponsemakes itFILTERforproxyToClientResponse; within one hook of aHttpFiltersChain, 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
Addsflagto 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'sResponseFlag.RATE_LIMITED, say). Does nothing on a context made outside the proxy.- Parameters:
flag- the flag to add
-
setResponseCodeDetails
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 asconcurrency_limited
-
trace
The distributed trace of the exchange in progress on this connection (or the last one), made by the proxy'sFlowTracer: its trace and span ids, and the span a filter may add attributes and events to.FlowTrace.NONEwithout a tracer, and for contexts made outside the proxy.- Returns:
- the exchange's trace
-
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 inActivityTracker.responseCompleted(org.microproxy.FlowContext, org.microproxy.http.HttpResponse)(orActivityTracker.flowEnded(org.microproxy.FlowRecord)) to see the whole exchange. Contexts made outside the proxy returnFlowTimings.NONE.- Returns:
- a snapshot of the exchange phase timings
-
getClientHello
The TLSClientHellothe client sent to start the intercepted (or tunnelled) session this flow belongs to: after aCONNECTwhose client was asked for one, and on a transparent TLS connection.nullbefore 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
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.NONEwhen 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 tooksteps- the interpreter steps it ranallocBytes- 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
Returns the client address and authentication details.- Returns:
- the client address and authentication details
-
setTimingData
Stores a named legacy timing value.- Parameters:
key- the timing measurement namevalue- the timing value to record
-
getTimingData
Returns a named legacy timing value.- Parameters:
key- the timing measurement name- Returns:
- the recorded value, or
nullif absent
-
getTimings
Returns the legacy timing values.- Returns:
- an immutable snapshot of the legacy timing map
-
equals
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. -
hashCode
public int hashCode()
-