Class ClientToProxyConnection
- All Implemented Interfaces:
ChannelHandler,ChannelInboundHandler
Represents a connection from a client to our proxy. Each
ClientToProxyConnection can have multiple ProxyToServerConnections,
at most one per outbound host:port.
Once a ProxyToServerConnection has been created for a given server, it is
continually reused. The ProxyToServerConnection goes through its own
lifecycle of connects and disconnects, with different underlying
Channels, but only a single ProxyToServerConnection object is used
per server. The one exception to this is CONNECT tunneling - if a connection
has been used for CONNECT tunneling, that connection will never be reused.
As the ProxyToServerConnections receive responses from their servers, they
feed these back to the client by calling
respond(ProxyToServerConnection, HttpFilters, HttpRequest, HttpResponse, HttpObject)
.
-
Nested Class Summary
Nested classes/interfaces inherited from class org.littleshoot.proxy.impl.ProxyConnection
ProxyConnection.BytesReadMonitor, ProxyConnection.BytesWrittenMonitor, ProxyConnection.RequestReadMonitor, ProxyConnection.RequestWrittenMonitor, ProxyConnection.ResponseReadMonitor, ProxyConnection.ResponseWrittenMonitorNested classes/interfaces inherited from interface io.netty.channel.ChannelHandler
ChannelHandler.Sharable -
Field Summary
FieldsModifier and TypeFieldDescriptionprivate static final PatternUsed for case-insensitive comparisons when checking direct proxy request.private final AtomicBooleanprivate final ProxyConnection<HttpRequest>.BytesReadMonitorprivate final ProxyConnection<HttpRequest>.BytesWrittenMonitorprivate final ClientDetailsprivate SSLSessionprivate static final HttpResponseStatusprivate HttpFiltersThe current filters to apply to incoming requests/chunks.private HttpRequestThe current HTTP request that this connection is currently servicing.private ProxyToServerConnectionThis is the current server connection that we're using while transferring chunked data.private final GlobalTrafficShapingHandlerprivate HAProxyMessageKeep track of proxy protocol headerprivate static final Stringprivate static final Stringprivate static final Stringprivate static final Stringprivate static final Stringprivate static final Stringprivate booleanTracks whether this ClientToProxyConnection is current doing MITM.private final AtomicIntegerKeep track of how many servers are currently connected.private final AtomicIntegerKeep track of how many servers are currently in the process of connecting.private final AtomicIntegerKeep track of how many times we were able to reuse a connection.private final ProxyConnection<HttpRequest>.RequestReadMonitor(package private) ConnectionFlowStepTells the Client that its HTTP CONNECT request was successful.private final ProxyConnection<HttpRequest>.ResponseWrittenMonitorprivate final Map<String,ProxyToServerConnection> Keep track of all ProxyToServerConnections by host+port.Fields inherited from class org.littleshoot.proxy.impl.ProxyConnection
channel, ctx, lastReadTime, LOG, proxyServer, runsAsSslClient, sslEngine, StartTunneling, tunneling -
Constructor Summary
ConstructorsConstructorDescriptionClientToProxyConnection(DefaultHttpProxyServer proxyServer, SslEngineSource sslEngineSource, boolean authenticateClients, ChannelPipeline pipeline, GlobalTrafficShapingHandler globalTrafficShapingHandler) -
Method Summary
Modifier and TypeMethodDescriptionprivate booleanauthenticationRequired(HttpRequest request) Checks whether the given HttpRequest requires authentication.protected voidWhen the ClientToProxyConnection becomes saturated, stop reading on all associated ProxyToServerConnections.protected voidWhen the ClientToProxyConnection becomes writable, resume reading on all associated ProxyToServerConnections.private voidcloseConnectionsAfterWriteIfNecessary(ProxyToServerConnection serverConnection, HttpRequest currentHttpRequest, HttpResponse currentHttpResponse, HttpObject httpObject) This method takes care of closing client to proxy and/or proxy to server connections after finishing a write.protected voidOn connect of the client, start waiting for an initialHttpRequest.private voidconnectionFailedUnrecoverably(HttpRequest initialRequest, ProxyToServerConnection serverConnection) private HttpRequestcopy(HttpRequest original) Copy the givenHttpRequestverbatim.protected voidOn disconnect of the client, disconnect all server connections.private ConnectionStatedoReadHTTPInitial(HttpRequest httpRequest) Reads anHttpRequest.protected voidexceptionCaught(Throwable cause) Override this to handle exceptions that occurred during asynchronous processing on theChannel.private voidfixHttpVersionHeaderIfNecessary(HttpResponse httpResponse) Chunked encoding is an HTTP 1.1 feature, but sometimes we get a chunked response that reports its HTTP version as 1.0.private FlowContextprivate voidforceDisconnect(ProxyToServerConnection serverConnection) private StringidentifyHostAndPort(HttpRequest httpRequest) Identify the host and port for a request.private voidinitChannelPipeline(ChannelPipeline pipeline) Initialize theChannelPipelinefor the client to proxy channel.(package private) booleanIs the proxy server set to accept a proxy protocol headerbooleanprivate booleanprivate booleanisRequestToOriginServer(HttpRequest httpRequest) Returns true if the specified request is a request to an origin server, rather than to a proxy server.(package private) booleanIs the proxy server set to send a proxy protocol headerprivate voidmodifyRequestHeadersToReflectProxying(HttpRequest httpRequest) If and only if our proxy is not running in transparent mode, modify the request headers to reflect that it was proxied.private voidmodifyResponseHeadersToReflectProxying(HttpResponse httpResponse) If and only if our proxy is not running in transparent mode, modify the response headers to reflect that it was proxied.protected voidRead anHAProxyMessageprotected voidreadHTTPChunk(HttpContent chunk) Implement this to handle reading a chunk in a chunked transfer.protected ConnectionStatereadHTTPInitial(HttpRequest httpRequest) Implement this to handle reading the initial object (e.g.protected voidImplement this to handle reading a raw buffer as they are used in HTTP tunneling.private voidprivate voidprivate voidprivate voidremoveHandlerIfPresent(String name) private void(package private) voidrespond(ProxyToServerConnection serverConnection, HttpFilters filters, HttpRequest currentHttpRequest, HttpResponse currentHttpResponse, HttpObject httpObject) Send a response to the client.private booleanrespondWithShortCircuitResponse(HttpResponse httpResponse) Responds to the client with the specified "short-circuit" response.private voidprotected voidserverBecameSaturated(ProxyToServerConnection serverConnection) When a server becomes saturated, we stop reading from the client.protected voidserverBecameWriteable(ProxyToServerConnection serverConnection) When a server becomes writeable, we check to see if all servers are writeable and if they are, we resume reading.protected booleanserverConnectionFailed(ProxyToServerConnection serverConnection, ConnectionState lastStateBeforeFailure, Throwable cause) If theProxyToServerConnectionfails to complete its connection lifecycle successfully, this method is called to let us know about it.protected voidserverConnectionFlowStarted(ProxyToServerConnection serverConnection) Called whenProxyToServerConnectionstarts its connection flow.protected voidserverConnectionSucceeded(ProxyToServerConnection serverConnection, boolean shouldForwardInitialRequest) If theProxyToServerConnectioncompletes its connection lifecycle successfully, this method is called to let us know about it.protected voidserverDisconnected(ProxyToServerConnection serverConnection) On disconnect of the server, track that we have one fewer connected servers and then disconnect the client if necessary.protected voidsetMitming(boolean isMitming) private booleanshouldCloseClientConnection(HttpRequest req, HttpResponse res, HttpObject httpObject) Determine whether or not the client connection should be closed.private booleanshouldCloseServerConnection(HttpRequest req, HttpResponse res, HttpObject msg) Determines if the remote connection should be closed based on the request and response pair.private voidstripConnectionTokens(HttpHeaders headers) RFC2616 Section 14.10 HTTP/1.1 proxies MUST parse the Connection header field before a message is forwarded and, for each connection-token in this field, remove any header field(s) from the message with the same name as the connection-token.private voidstripHopByHopHeaders(HttpHeaders headers) Removes all headers that should not be forwarded.private voidswitchProxyConnectionHeader(HttpHeaders headers) Switch the de-facto standard "Proxy-Connection" header to "Connection" when we pass it along to the remote host.private voidswitchToWebSocketProtocol(ProxyToServerConnection serverConnection) protected voidtimedOut()This method is called when the underlyingChanneltimes out due to an idle timeout.(package private) voidtimedOut(ProxyToServerConnection serverConnection) private voidprivate booleanwriteBadGateway(HttpRequest httpRequest) Tells the client that something went wrong trying to proxy its request.private booleanwriteBadRequest(HttpRequest httpRequest) Tells the client that the request was malformed or erroneous.private ChannelFutureWrite an empty buffer at the end of a chunked transfer.private booleanwriteGatewayTimeout(HttpRequest httpRequest) Tells the client that the connection to the server, or possibly to some intermediary service (such as DNS), timed out.Methods inherited from class org.littleshoot.proxy.impl.ProxyConnection
aggregateContentForFiltering, become, channelActive, channelInactive, channelRead0, channelRegistered, channelUnregistered, channelWritabilityChanged, disconnect, doWrite, encrypt, encrypt, EncryptChannel, exceptionCaught, getCurrentState, getHttpFiltersFromProxyServer, getLOG, getSslEngine, is, isConnecting, isSaturated, isTunneling, read, removeHandlerIfPresent, resumeReading, stopReading, userEventTriggered, write, writeHttp, writeRaw, writeToChannelMethods inherited from class io.netty.channel.SimpleChannelInboundHandler
acceptInboundMessage, channelReadMethods inherited from class io.netty.channel.ChannelInboundHandlerAdapter
channelReadCompleteMethods inherited from class io.netty.channel.ChannelHandlerAdapter
ensureNotSharable, handlerAdded, handlerRemoved, isSharableMethods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, waitMethods inherited from interface io.netty.channel.ChannelHandler
handlerAdded, handlerRemoved
-
Field Details
-
CONNECTION_ESTABLISHED
-
HTTP_ENCODER_NAME
- See Also:
-
HTTP_DECODER_NAME
- See Also:
-
HTTP_PROXY_DECODER_NAME
- See Also:
-
HTTP_REQUEST_READ_MONITOR_NAME
- See Also:
-
HTTP_RESPONSE_WRITTEN_MONITOR_NAME
- See Also:
-
MAIN_HANDLER_NAME
- See Also:
-
ABSOLUTE_URI_PATTERN
Used for case-insensitive comparisons when checking direct proxy request. -
serverConnectionsByHostAndPort
Keep track of all ProxyToServerConnections by host+port. -
numberOfCurrentlyConnectingServers
Keep track of how many servers are currently in the process of connecting. -
haProxyMessage
Keep track of proxy protocol header -
numberOfCurrentlyConnectedServers
Keep track of how many servers are currently connected. -
numberOfReusedServerConnections
Keep track of how many times we were able to reuse a connection. -
currentServerConnection
This is the current server connection that we're using while transferring chunked data. -
currentFilters
The current filters to apply to incoming requests/chunks. -
clientSslSession
-
mitming
private volatile boolean mitmingTracks whether this ClientToProxyConnection is current doing MITM. -
authenticated
-
globalTrafficShapingHandler
-
currentRequest
The current HTTP request that this connection is currently servicing. -
clientDetails
-
RespondCONNECTSuccessful
ConnectionFlowStep RespondCONNECTSuccessfulTells the Client that its HTTP CONNECT request was successful. -
bytesReadMonitor
-
requestReadMonitor
-
bytesWrittenMonitor
-
responseWrittenMonitor
-
-
Constructor Details
-
ClientToProxyConnection
ClientToProxyConnection(DefaultHttpProxyServer proxyServer, SslEngineSource sslEngineSource, boolean authenticateClients, ChannelPipeline pipeline, GlobalTrafficShapingHandler globalTrafficShapingHandler)
-
-
Method Details
-
readHAProxyMessage
Description copied from class:ProxyConnectionRead anHAProxyMessage- Specified by:
readHAProxyMessagein classProxyConnection<HttpRequest>- Parameters:
msg-HAProxyMessage
-
readHTTPInitial
Description copied from class:ProxyConnectionImplement this to handle reading the initial object (e.g.HttpRequestorHttpResponse).- Specified by:
readHTTPInitialin classProxyConnection<HttpRequest>
-
doReadHTTPInitial
Reads an
HttpRequest.If we don't yet have a
ProxyToServerConnectionfor the desired server, this takes care of creating it.Note - the "server" could be a chained proxy, not the final endpoint for the request.
-
isRequestToOriginServer
Returns true if the specified request is a request to an origin server, rather than to a proxy server. If this request is being MITM'd, this method always returns false. The format of requests to a proxy server are defined in RFC 7230, section 5.3.2 (all other requests are considered requests to an origin server):When making a request to a proxy, other than a CONNECT or server-wide OPTIONS request (as detailed below), a client MUST send the target URI in absolute-form as the request-target. [...] An example absolute-form of request-line would be: GET https://www.example.org/pub/WWW/TheProject.html HTTP/1.1 To allow for transition to the absolute-form for all requests in some future version of HTTP, a server MUST accept the absolute-form in requests, even though HTTP/1.1 clients will only send them in requests to proxies.- Parameters:
httpRequest- the request to evaluate- Returns:
- true if the specified request is a request to an origin server, otherwise false
-
readHTTPChunk
Description copied from class:ProxyConnectionImplement this to handle reading a chunk in a chunked transfer.- Specified by:
readHTTPChunkin classProxyConnection<HttpRequest>
-
readRaw
Description copied from class:ProxyConnectionImplement this to handle reading a raw buffer as they are used in HTTP tunneling.- Specified by:
readRawin classProxyConnection<HttpRequest>
-
respond
void respond(ProxyToServerConnection serverConnection, HttpFilters filters, HttpRequest currentHttpRequest, HttpResponse currentHttpResponse, HttpObject httpObject) Send a response to the client.- Parameters:
serverConnection- the ProxyToServerConnection that's respondingfilters- the filters to apply to the responsecurrentHttpRequest- the HttpRequest that prompted this responsecurrentHttpResponse- the HttpResponse corresponding to this data (when doing chunked transfers, this is the initial HttpResponse object that came in before the other chunks)httpObject- the data with which to respond
-
resetCurrentRequest
private void resetCurrentRequest() -
switchToWebSocketProtocol
-
connected
protected void connected()On connect of the client, start waiting for an initialHttpRequest.- Overrides:
connectedin classProxyConnection<HttpRequest>
-
timedOut
-
timedOut
protected void timedOut()Description copied from class:ProxyConnectionThis method is called when the underlyingChanneltimes out due to an idle timeout.- Overrides:
timedOutin classProxyConnection<HttpRequest>
-
disconnected
protected void disconnected()On disconnect of the client, disconnect all server connections.- Overrides:
disconnectedin classProxyConnection<HttpRequest>
-
serverConnectionFlowStarted
Called whenProxyToServerConnectionstarts its connection flow. -
serverConnectionSucceeded
protected void serverConnectionSucceeded(ProxyToServerConnection serverConnection, boolean shouldForwardInitialRequest) If theProxyToServerConnectioncompletes its connection lifecycle successfully, this method is called to let us know about it. -
serverConnectionFailed
protected boolean serverConnectionFailed(ProxyToServerConnection serverConnection, ConnectionState lastStateBeforeFailure, Throwable cause) If theProxyToServerConnectionfails to complete its connection lifecycle successfully, this method is called to let us know about it.After failing to connect to the server, one of two things can happen:
- If the server was a chained proxy, we fall back to connecting to the ultimate endpoint directly.
- If the server was the ultimate endpoint, we return a 502 Bad Gateway to the client.
- Parameters:
serverConnection-lastStateBeforeFailure-cause- what caused the failure- Returns:
- true if we're falling back to a another chained proxy (or direct connection) and trying again
-
connectionFailedUnrecoverably
private void connectionFailedUnrecoverably(HttpRequest initialRequest, ProxyToServerConnection serverConnection) -
resumeReadingIfNecessary
private void resumeReadingIfNecessary() -
serverDisconnected
On disconnect of the server, track that we have one fewer connected servers and then disconnect the client if necessary. -
becameSaturated
protected void becameSaturated()When the ClientToProxyConnection becomes saturated, stop reading on all associated ProxyToServerConnections.- Overrides:
becameSaturatedin classProxyConnection<HttpRequest>
-
becameWritable
protected void becameWritable()When the ClientToProxyConnection becomes writable, resume reading on all associated ProxyToServerConnections.- Overrides:
becameWritablein classProxyConnection<HttpRequest>
-
serverBecameSaturated
When a server becomes saturated, we stop reading from the client. -
serverBecameWriteable
When a server becomes writeable, we check to see if all servers are writeable and if they are, we resume reading. -
exceptionCaught
Description copied from class:ProxyConnectionOverride this to handle exceptions that occurred during asynchronous processing on theChannel.- Overrides:
exceptionCaughtin classProxyConnection<HttpRequest>
-
initChannelPipeline
Initialize theChannelPipelinefor the client to proxy channel. LittleProxy acts like a server here. AChannelPipelineinvokes the read (Inbound) handlers in ascending ordering of the list and then the write (Outbound) handlers in descending ordering. Regarding the Javadoc ofHttpObjectAggregatorit's needed to have theHttpResponseEncoderorHttpRequestEncoderbefore theHttpObjectAggregatorin theChannelPipeline. -
removeHandlerIfPresent
-
isAcceptProxyProtocol
boolean isAcceptProxyProtocol()Is the proxy server set to accept a proxy protocol header- Returns:
- True if the proxy server set to accept a proxy protocol header. False otherwise
-
isSendProxyProtocol
boolean isSendProxyProtocol()Is the proxy server set to send a proxy protocol header- Returns:
- True if the proxy server set to send a proxy protocol header. False otherwise
-
closeConnectionsAfterWriteIfNecessary
private void closeConnectionsAfterWriteIfNecessary(ProxyToServerConnection serverConnection, HttpRequest currentHttpRequest, HttpResponse currentHttpResponse, HttpObject httpObject) This method takes care of closing client to proxy and/or proxy to server connections after finishing a write. -
forceDisconnect
-
shouldCloseClientConnection
private boolean shouldCloseClientConnection(HttpRequest req, HttpResponse res, HttpObject httpObject) Determine whether or not the client connection should be closed. -
shouldCloseServerConnection
Determines if the remote connection should be closed based on the request and response pair. If the request is HTTP 1.0 with no keep-alive header, for example, the connection should be closed. This in part determines if we should close the connection. Here's the relevant section of RFC 2616: "HTTP/1.1 defines the "close" connection option for the sender to signal that the connection will be closed after completion of the response. For example, Connection: close in either the request or the response header fields indicates that the connection SHOULD NOT be considered `persistent' (section 8.1) after the current request/response is complete."- Parameters:
req- The request.res- The response.msg- The message.- Returns:
- Returns true if the connection should close.
-
authenticationRequired
Checks whether the given HttpRequest requires authentication.
If the request contains credentials, these are checked.
If authentication is still required, either because no credentials were provided or the credentials were wrong, this writes a 407 response to the client.
-
writeAuthenticationRequired
-
copy
Copy the givenHttpRequestverbatim. -
fixHttpVersionHeaderIfNecessary
Chunked encoding is an HTTP 1.1 feature, but sometimes we get a chunked response that reports its HTTP version as 1.0. In this case, we change it to 1.1. -
modifyRequestHeadersToReflectProxying
If and only if our proxy is not running in transparent mode, modify the request headers to reflect that it was proxied. -
isNextHopOriginServer
private boolean isNextHopOriginServer() -
modifyResponseHeadersToReflectProxying
If and only if our proxy is not running in transparent mode, modify the response headers to reflect that it was proxied. -
switchProxyConnectionHeader
Switch the de-facto standard "Proxy-Connection" header to "Connection" when we pass it along to the remote host. This is largely undocumented but seems to be what most browsers and servers expect.- Parameters:
headers- The headers to modify
-
stripConnectionTokens
RFC2616 Section 14.10 HTTP/1.1 proxies MUST parse the Connection header field before a message is forwarded and, for each connection-token in this field, remove any header field(s) from the message with the same name as the connection-token.- Parameters:
headers- The headers to modify
-
stripHopByHopHeaders
Removes all headers that should not be forwarded. See RFC 2616 13.5.1 End-to-end and Hop-by-hop Headers.- Parameters:
headers- The headers to modify
-
writeBadGateway
Tells the client that something went wrong trying to proxy its request. If the Bad Gateway is a response to an HTTP HEAD request, the response will contain no body, but the Content-Length header will be set to the value it would have been if this 502 Bad Gateway were in response to a GET.- Parameters:
httpRequest- the HttpRequest that is resulting in the Bad Gateway response- Returns:
- true if the connection will be kept open, or false if it will be disconnected
-
writeBadRequest
Tells the client that the request was malformed or erroneous. If the Bad Request is a response to an HTTP HEAD request, the response will contain no body, but the Content-Length header will be set to the value it would have been if this Bad Request were in response to a GET.- Returns:
- true if the connection will be kept open, or false if it will be disconnected
-
writeGatewayTimeout
Tells the client that the connection to the server, or possibly to some intermediary service (such as DNS), timed out. If the Gateway Timeout is a response to an HTTP HEAD request, the response will contain no body, but the Content-Length header will be set to the value it would have been if this 504 Gateway Timeout were in response to a GET.- Parameters:
httpRequest- the HttpRequest that is resulting in the Gateway Timeout response- Returns:
- true if the connection will be kept open, or false if it will be disconnected
-
respondWithShortCircuitResponse
Responds to the client with the specified "short-circuit" response. The response will be sent through theHttpFilters.proxyToClientResponse(HttpObject)filter method before writing it to the client. The client will not be disconnected, unless the response includes a "Connection: close" header, or the filter returns a null HttpResponse (in which case no response will be written to the client and the connection will be disconnected immediately). If the response is not a Bad Gateway or Gateway Timeout response, the response's headers will be modified to reflect proxying, including adding a Via header, Date header, etc.- Parameters:
httpResponse- the response to return to the client- Returns:
- true if the connection will be kept open, or false if it will be disconnected.
-
identifyHostAndPort
Identify the host and port for a request. -
writeEmptyBuffer
Write an empty buffer at the end of a chunked transfer. We need to do this to handle the way Netty creates HttpChunks from responses that aren't in fact chunked from the remote server using Transfer-Encoding: chunked. Netty turns these into pseudo-chunked responses in cases where the response would otherwise fill up too much memory or where the length of the response body is unknown. This is handy because it means we can start streaming response bodies back to the client without reading the entire response. The problem is that in these pseudo-cases the last chunk is encoded to null, and this thwarts normal ChannelFutures from propagating operationComplete events on writes to appropriate channel listeners. We work around this by writing an empty buffer in those cases and using the empty buffer's future instead to handle any operations we need to when responses are fully written back to clients. -
isMitming
public boolean isMitming() -
setMitming
protected void setMitming(boolean isMitming) -
recordClientConnected
private void recordClientConnected() -
recordClientSSLHandshakeSucceeded
private void recordClientSSLHandshakeSucceeded() -
recordClientDisconnected
private void recordClientDisconnected() -
getClientAddress
-
flowContext
-
getHaProxyMessage
-
getClientDetails
-