org.littleshoot.proxy.impl
Class ClientToProxyConnection

java.lang.Object
  extended by io.netty.channel.ChannelHandlerAdapter
      extended by io.netty.channel.ChannelInboundHandlerAdapter
          extended by io.netty.channel.SimpleChannelInboundHandler<Object>
              extended by org.littleshoot.proxy.impl.ClientToProxyConnection
All Implemented Interfaces:
io.netty.channel.ChannelHandler, io.netty.channel.ChannelInboundHandler

public class ClientToProxyConnection
extends io.netty.channel.SimpleChannelInboundHandler<Object>

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 interface io.netty.channel.ChannelHandler
io.netty.channel.ChannelHandler.Sharable
 
Field Summary
protected  io.netty.channel.Channel channel
           
protected  io.netty.channel.ChannelHandlerContext ctx
           
protected  long lastReadTime
           
protected  org.littleshoot.proxy.impl.ProxyConnectionLogger LOG
           
protected  DefaultHttpProxyServer proxyServer
           
protected  boolean runsAsSslClient
           
protected  SSLEngine sslEngine
          If using encryption, this holds our SSLEngine.
protected  org.littleshoot.proxy.impl.ConnectionFlowStep StartTunneling
           Enables tunneling on this connection by dropping the HTTP related encoders and decoders, as well as idle timers.
 
Method Summary
protected  void aggregateContentForFiltering(io.netty.channel.ChannelPipeline pipeline, int numberOfBytesToBuffer)
          Enables decompression and aggregation of content, which is useful for certain types of filtering activity.
protected  void becameSaturated()
          When the ClientToProxyConnection becomes saturated, stop reading on all associated ProxyToServerConnections.
protected  void becameWritable()
          When the ClientToProxyConnection becomes writable, resume reading on all associated ProxyToServerConnections.
protected  void become(org.littleshoot.proxy.impl.ConnectionState state)
          Udpates the current state to the given value.
 void channelActive(io.netty.channel.ChannelHandlerContext ctx)
          Only once the Netty Channel is active to we recognize the ProxyConnection as connected.
 void channelInactive(io.netty.channel.ChannelHandlerContext ctx)
          As soon as the Netty Channel is inactive, we recognize the ProxyConnection as disconnected.
protected  void channelRead0(io.netty.channel.ChannelHandlerContext ctx, Object msg)
          Adapting the Netty API
 void channelRegistered(io.netty.channel.ChannelHandlerContext ctx)
           
 void channelWritabilityChanged(io.netty.channel.ChannelHandlerContext ctx)
           
protected  void connected()
          On connect of the client, start waiting for an initial HttpRequest.
protected  void disconnected()
          On disconnect of the client, disconnect all server connections.
protected  io.netty.util.concurrent.Future<io.netty.channel.Channel> encrypt(io.netty.channel.ChannelPipeline pipeline, SSLEngine sslEngine, boolean authenticateClients)
          Encrypts traffic on this connection with SSL/TLS.
protected  io.netty.util.concurrent.Future<io.netty.channel.Channel> encrypt(SSLEngine sslEngine, boolean authenticateClients)
          Encrypts traffic on this connection with SSL/TLS.
protected  org.littleshoot.proxy.impl.ConnectionFlowStep EncryptChannel(SSLEngine sslEngine)
          Encrypts the channel using the provided SSLEngine.
 void exceptionCaught(io.netty.channel.ChannelHandlerContext ctx, Throwable cause)
           
protected  void exceptionCaught(Throwable cause)
          Override this to handle exceptions that occurred during asynchronous processing on the Channel.
 InetSocketAddress getClientAddress()
           
protected  org.littleshoot.proxy.impl.ConnectionState getCurrentState()
           
 SSLEngine getSslEngine()
           
protected  boolean is(org.littleshoot.proxy.impl.ConnectionState state)
          Utility for checking current state.
protected  boolean isConnecting()
          If this connection is currently in the process of going through a ConnectionFlow, this will return true.
 boolean isMitming()
           
protected  boolean isSaturated()
          Indicates whether or not this connection is saturated (i.e.
 boolean isTunneling()
           
protected  void read(Object msg)
          Read is invoked automatically by Netty as messages arrive on the socket.
protected  void readHTTPChunk(io.netty.handler.codec.http.HttpContent chunk)
          Implement this to handle reading a chunk in a chunked transfer.
protected  org.littleshoot.proxy.impl.ConnectionState readHTTPInitial(io.netty.handler.codec.http.HttpRequest httpRequest)
          Reading
protected  void readRaw(io.netty.buffer.ByteBuf buf)
          Implement this to handle reading a raw buffer as they are used in HTTP tunneling.
protected  void resumeReading()
          Call this to resume reading.
protected  void serverBecameSaturated(ProxyToServerConnection serverConnection)
          When a server becomes saturated, we stop reading from the client.
protected  void serverBecameWriteable(ProxyToServerConnection serverConnection)
          When a server becomes writeable, we check to see if all servers are writeable and if they are, we resume reading.
protected  boolean serverConnectionFailed(ProxyToServerConnection serverConnection, org.littleshoot.proxy.impl.ConnectionState lastStateBeforeFailure, Throwable cause)
          If the ProxyToServerConnection fails to complete its connection lifecycle successfully, this method is called to let us know about it.
protected  void serverConnectionFlowStarted(ProxyToServerConnection serverConnection)
          Called when ProxyToServerConnection starts its connection flow.
protected  void serverConnectionSucceeded(ProxyToServerConnection serverConnection, boolean shouldForwardInitialRequest)
          If the ProxyToServerConnection completes its connection lifecycle successfully, this method is called to let us know about it.
protected  void serverDisconnected(ProxyToServerConnection serverConnection)
          On disconnect of the server, track that we have one fewer connected servers and then disconnect the client if necessary.
protected  void setMitming(boolean isMitming)
           
protected  void stopReading()
          Call this to stop reading.
protected  void timedOut()
          This method is called when the underlying Channel times out due to an idle timeout.
 void userEventTriggered(io.netty.channel.ChannelHandlerContext ctx, Object evt)
           We're looking for IdleStateEvents to see if we need to disconnect.
protected  void writeHttp(io.netty.handler.codec.http.HttpObject httpObject)
          Writes HttpObjects to the connection asynchronously.
protected  void writeRaw(io.netty.buffer.ByteBuf buf)
          Writes raw buffers to the connection.
protected  io.netty.channel.ChannelFuture writeToChannel(Object msg)
           
 
Methods inherited from class io.netty.channel.SimpleChannelInboundHandler
acceptInboundMessage, channelRead
 
Methods inherited from class io.netty.channel.ChannelInboundHandlerAdapter
channelReadComplete, channelUnregistered
 
Methods inherited from class io.netty.channel.ChannelHandlerAdapter
handlerAdded, handlerRemoved, isSharable
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 
Methods inherited from interface io.netty.channel.ChannelHandler
handlerAdded, handlerRemoved
 

Field Detail

LOG

protected final org.littleshoot.proxy.impl.ProxyConnectionLogger LOG

proxyServer

protected final DefaultHttpProxyServer proxyServer

runsAsSslClient

protected final boolean runsAsSslClient

ctx

protected volatile io.netty.channel.ChannelHandlerContext ctx

channel

protected volatile io.netty.channel.Channel channel

lastReadTime

protected volatile long lastReadTime

sslEngine

protected volatile SSLEngine sslEngine
If using encryption, this holds our SSLEngine.


StartTunneling

protected org.littleshoot.proxy.impl.ConnectionFlowStep StartTunneling

Enables tunneling on this connection by dropping the HTTP related encoders and decoders, as well as idle timers.

Note - the work is done on the ChannelHandlerContext's executor because ChannelPipeline.remove(String) can deadlock if called directly.

Method Detail

readHTTPInitial

protected org.littleshoot.proxy.impl.ConnectionState readHTTPInitial(io.netty.handler.codec.http.HttpRequest httpRequest)
Reading

Returns:

readHTTPChunk

protected void readHTTPChunk(io.netty.handler.codec.http.HttpContent chunk)
Implement this to handle reading a chunk in a chunked transfer.


readRaw

protected void readRaw(io.netty.buffer.ByteBuf buf)
Implement this to handle reading a raw buffer as they are used in HTTP tunneling.


connected

protected void connected()
On connect of the client, start waiting for an initial HttpRequest.


timedOut

protected void timedOut()
This method is called when the underlying Channel times out due to an idle timeout.


disconnected

protected void disconnected()
On disconnect of the client, disconnect all server connections.


serverConnectionFlowStarted

protected void serverConnectionFlowStarted(ProxyToServerConnection serverConnection)
Called when ProxyToServerConnection starts its connection flow.

Parameters:
serverConnection -

serverConnectionSucceeded

protected void serverConnectionSucceeded(ProxyToServerConnection serverConnection,
                                         boolean shouldForwardInitialRequest)
If the ProxyToServerConnection completes its connection lifecycle successfully, this method is called to let us know about it.

Parameters:
serverConnection -
shouldForwardInitialRequest -

serverConnectionFailed

protected boolean serverConnectionFailed(ProxyToServerConnection serverConnection,
                                         org.littleshoot.proxy.impl.ConnectionState lastStateBeforeFailure,
                                         Throwable cause)
If the ProxyToServerConnection fails 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:

  1. If the server was a chained proxy, we fall back to connecting to the ultimate endpoint directly.
  2. 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

serverDisconnected

protected void serverDisconnected(ProxyToServerConnection serverConnection)
On disconnect of the server, track that we have one fewer connected servers and then disconnect the client if necessary.

Parameters:
serverConnection -

becameSaturated

protected void becameSaturated()
When the ClientToProxyConnection becomes saturated, stop reading on all associated ProxyToServerConnections.


becameWritable

protected void becameWritable()
When the ClientToProxyConnection becomes writable, resume reading on all associated ProxyToServerConnections.


serverBecameSaturated

protected void serverBecameSaturated(ProxyToServerConnection serverConnection)
When a server becomes saturated, we stop reading from the client.

Parameters:
serverConnection -

serverBecameWriteable

protected void serverBecameWriteable(ProxyToServerConnection serverConnection)
When a server becomes writeable, we check to see if all servers are writeable and if they are, we resume reading.

Parameters:
serverConnection -

exceptionCaught

protected void exceptionCaught(Throwable cause)
Override this to handle exceptions that occurred during asynchronous processing on the Channel.


isMitming

public boolean isMitming()

setMitming

protected void setMitming(boolean isMitming)

getClientAddress

public InetSocketAddress getClientAddress()

read

protected void read(Object msg)
Read is invoked automatically by Netty as messages arrive on the socket.

Parameters:
msg -

writeHttp

protected void writeHttp(io.netty.handler.codec.http.HttpObject httpObject)
Writes HttpObjects to the connection asynchronously.

Parameters:
httpObject -

writeRaw

protected void writeRaw(io.netty.buffer.ByteBuf buf)
Writes raw buffers to the connection.

Parameters:
buf -

writeToChannel

protected io.netty.channel.ChannelFuture writeToChannel(Object msg)

encrypt

protected io.netty.util.concurrent.Future<io.netty.channel.Channel> encrypt(SSLEngine sslEngine,
                                                                            boolean authenticateClients)
Encrypts traffic on this connection with SSL/TLS.

Parameters:
sslEngine - the SSLEngine for doing the encryption
authenticateClients - determines whether to authenticate clients or not
Returns:
a Future for when the SSL handshake has completed

encrypt

protected io.netty.util.concurrent.Future<io.netty.channel.Channel> encrypt(io.netty.channel.ChannelPipeline pipeline,
                                                                            SSLEngine sslEngine,
                                                                            boolean authenticateClients)
Encrypts traffic on this connection with SSL/TLS.

Parameters:
pipeline - the ChannelPipeline on which to enable encryption
sslEngine - the SSLEngine for doing the encryption
authenticateClients - determines whether to authenticate clients or not
Returns:
a Future for when the SSL handshake has completed

EncryptChannel

protected org.littleshoot.proxy.impl.ConnectionFlowStep EncryptChannel(SSLEngine sslEngine)
Encrypts the channel using the provided SSLEngine.

Parameters:
sslEngine - the SSLEngine for doing the encryption

aggregateContentForFiltering

protected void aggregateContentForFiltering(io.netty.channel.ChannelPipeline pipeline,
                                            int numberOfBytesToBuffer)
Enables decompression and aggregation of content, which is useful for certain types of filtering activity.

Parameters:
pipeline -
numberOfBytesToBuffer -

isSaturated

protected boolean isSaturated()
Indicates whether or not this connection is saturated (i.e. not writeable).

Returns:

is

protected boolean is(org.littleshoot.proxy.impl.ConnectionState state)
Utility for checking current state.

Parameters:
state -
Returns:

isConnecting

protected boolean isConnecting()
If this connection is currently in the process of going through a ConnectionFlow, this will return true.

Returns:

become

protected void become(org.littleshoot.proxy.impl.ConnectionState state)
Udpates the current state to the given value.

Parameters:
state -

getCurrentState

protected org.littleshoot.proxy.impl.ConnectionState getCurrentState()

isTunneling

public boolean isTunneling()

getSslEngine

public SSLEngine getSslEngine()

stopReading

protected void stopReading()
Call this to stop reading.


resumeReading

protected void resumeReading()
Call this to resume reading.


channelRead0

protected final void channelRead0(io.netty.channel.ChannelHandlerContext ctx,
                                  Object msg)
                           throws Exception
Adapting the Netty API

Specified by:
channelRead0 in class io.netty.channel.SimpleChannelInboundHandler<Object>
Throws:
Exception

channelRegistered

public void channelRegistered(io.netty.channel.ChannelHandlerContext ctx)
                       throws Exception
Specified by:
channelRegistered in interface io.netty.channel.ChannelInboundHandler
Overrides:
channelRegistered in class io.netty.channel.ChannelInboundHandlerAdapter
Throws:
Exception

channelActive

public final void channelActive(io.netty.channel.ChannelHandlerContext ctx)
                         throws Exception
Only once the Netty Channel is active to we recognize the ProxyConnection as connected.

Specified by:
channelActive in interface io.netty.channel.ChannelInboundHandler
Overrides:
channelActive in class io.netty.channel.ChannelInboundHandlerAdapter
Throws:
Exception

channelInactive

public void channelInactive(io.netty.channel.ChannelHandlerContext ctx)
                     throws Exception
As soon as the Netty Channel is inactive, we recognize the ProxyConnection as disconnected.

Specified by:
channelInactive in interface io.netty.channel.ChannelInboundHandler
Overrides:
channelInactive in class io.netty.channel.ChannelInboundHandlerAdapter
Throws:
Exception

channelWritabilityChanged

public final void channelWritabilityChanged(io.netty.channel.ChannelHandlerContext ctx)
                                     throws Exception
Specified by:
channelWritabilityChanged in interface io.netty.channel.ChannelInboundHandler
Overrides:
channelWritabilityChanged in class io.netty.channel.ChannelInboundHandlerAdapter
Throws:
Exception

exceptionCaught

public final void exceptionCaught(io.netty.channel.ChannelHandlerContext ctx,
                                  Throwable cause)
                           throws Exception
Specified by:
exceptionCaught in interface io.netty.channel.ChannelHandler
Specified by:
exceptionCaught in interface io.netty.channel.ChannelInboundHandler
Overrides:
exceptionCaught in class io.netty.channel.ChannelInboundHandlerAdapter
Throws:
Exception

userEventTriggered

public final void userEventTriggered(io.netty.channel.ChannelHandlerContext ctx,
                                     Object evt)
                              throws Exception

We're looking for IdleStateEvents to see if we need to disconnect.

Note - we don't care what kind of IdleState we got. Thanks to qbast for pointing this out.

Specified by:
userEventTriggered in interface io.netty.channel.ChannelInboundHandler
Overrides:
userEventTriggered in class io.netty.channel.ChannelInboundHandlerAdapter
Throws:
Exception


Copyright © 2009-2014 LittleShoot. All Rights Reserved.