429734 Implemented the HA ProxyProtocol

This commit is contained in:
Greg Wilkins 2014-04-04 08:47:37 +11:00
parent 6e9879cc6b
commit 4dbabd72f2
5 changed files with 171 additions and 8 deletions

View File

@ -38,7 +38,8 @@ public enum HttpMethod
DELETE,
TRACE,
CONNECT,
MOVE;
MOVE,
PROXY;
/* ------------------------------------------------------------ */
/**
@ -48,7 +49,7 @@ public enum HttpMethod
* @param limit The first non valid index
* @return A HttpMethod if a match or null if no easy match.
*/
public static HttpMethod lookAheadGet(byte[] bytes, int position, int limit)
public static HttpMethod lookAheadGet(byte[] bytes, final int position, int limit)
{
int length=limit-position;
if (length<4)
@ -62,6 +63,8 @@ public enum HttpMethod
case 'P':
if (bytes[position+1]=='O' && bytes[position+2]=='S' && bytes[position+3]=='T' && length>=5 && bytes[position+4]==' ')
return POST;
if (bytes[position+1]=='R' && bytes[position+2]=='O' && bytes[position+3]=='X' && length>=6 && bytes[position+4]=='Y' && bytes[position+5]==' ')
return PROXY;
if (bytes[position+1]=='U' && bytes[position+2]=='T' && bytes[position+3]==' ')
return PUT;
break;

View File

@ -104,6 +104,7 @@ public class HttpParser
SPACE2,
REQUEST_VERSION,
REASON,
PROXY,
HEADER,
HEADER_IN_NAME,
HEADER_VALUE,
@ -403,7 +404,7 @@ public class HttpParser
* otherwise skip white space until something else to parse.
*/
private boolean quickStart(ByteBuffer buffer)
{
{
if (_requestHandler!=null)
{
_method = HttpMethod.lookAheadGet(buffer);
@ -411,6 +412,7 @@ public class HttpParser
{
_methodString = _method.asString();
buffer.position(buffer.position()+_methodString.length()+1);
setState(State.SPACE1);
return false;
}
@ -655,7 +657,29 @@ public class HttpParser
version=HttpVersion.lookAheadGet(buffer.array(),buffer.arrayOffset()+buffer.position()-1,buffer.arrayOffset()+buffer.limit());
else
version=HttpVersion.CACHE.getBest(buffer,0,buffer.remaining());
if (version!=null)
if (version==null)
{
if (_method==HttpMethod.PROXY)
{
if (!(_requestHandler instanceof ProxyHandler))
throw new BadMessage();
_uri.flip();
String protocol=BufferUtil.toString(_uri);
// This is the proxy protocol, so we can assume entire first line is in buffer else 400
buffer.position(buffer.position()-1);
String sAddr = getProxyField(buffer);
String dAddr = getProxyField(buffer);
int sPort = BufferUtil.takeInt(buffer);
next(buffer);
int dPort = BufferUtil.takeInt(buffer);
next(buffer);
_state=State.START;
((ProxyHandler)_requestHandler).proxied(protocol,sAddr,dAddr,sPort,dPort);
return false;
}
}
else
{
int pos = buffer.position()+version.asString().length()-1;
if (pos<buffer.limit())
@ -715,8 +739,7 @@ public class HttpParser
if (_connectionFields==null && _version.getVersion()>=HttpVersion.HTTP_1_1.getVersion())
{
int header_cache = _handler.getHeaderCacheSize();
if (header_cache>0)
_connectionFields=new ArrayTernaryTrie<>(header_cache);
_connectionFields=new ArrayTernaryTrie<>(header_cache);
}
setState(State.HEADER);
@ -1586,6 +1609,11 @@ public class HttpParser
public int getHeaderCacheSize();
}
public interface ProxyHandler
{
void proxied(String protocol, String sAddr, String dAddr, int sPort, int dPort);
}
public interface RequestHandler<T> extends HttpHandler<T>
{
/**
@ -1618,4 +1646,20 @@ public class HttpParser
{
return _connectionFields;
}
private String getProxyField(ByteBuffer buffer)
{
_string.setLength(0);
_length=0;
while (buffer.hasRemaining())
{
// process each character
byte ch=next(buffer);
if (ch<=' ')
return _string.toString();
_string.append((char)ch);
}
throw new BadMessage();
}
}

View File

@ -1398,6 +1398,63 @@ public class HttpParserTest
}
@Test
public void testProxyProtocol() throws Exception
{
ByteBuffer buffer=BufferUtil
.toBuffer("PROXY TCP4 107.47.45.254 10.0.1.116 27689 80\015\012"
+"GET / HTTP/1.1\015\012"
+"Host: localhost \015\012"
+"Connection: close\015\012"+"\015\012"+"\015\012");
Handler handler=new Handler();
HttpParser parser=new HttpParser((HttpParser.RequestHandler)handler);
parseAll(parser, buffer);
assertTrue(_headerCompleted);
assertTrue(_messageCompleted);
assertEquals("GET", _methodOrVersion);
assertEquals("/", _uriOrStatus);
assertEquals("HTTP/1.1", _versionOrReason);
assertEquals("PROXY TCP4 107.47.45.254 10.0.1.116 27689 80", handler._proxy);
assertEquals("Host", _hdr[0]);
assertEquals("localhost", _val[0]);
assertEquals("Connection", _hdr[1]);
assertEquals("close", _val[1]);
assertEquals(1, _headers);
}
@Test
public void testSplitProxyHeaderParseTest() throws Exception
{
Handler handler=new Handler();
HttpParser parser=new HttpParser((HttpParser.RequestHandler)handler);
ByteBuffer buffer=BufferUtil.toBuffer("PROXY TCP4 207.47.45.254 10.0.1.116 27689 80\015\012");
parser.parseNext(buffer);
buffer=BufferUtil.toBuffer(
"GET / HTTP/1.1\015\012"
+"Host: localhost \015\012"
+"Connection: close\015\012"
+"\015\012"
+"\015\012");
parser.parseNext(buffer);
assertTrue(_headerCompleted);
assertTrue(_messageCompleted);
assertEquals("GET", _methodOrVersion);
assertEquals("/", _uriOrStatus);
assertEquals("HTTP/1.1", _versionOrReason);
assertEquals("PROXY TCP4 207.47.45.254 10.0.1.116 27689 80", handler._proxy);
assertEquals("Host", _hdr[0]);
assertEquals("localhost", _val[0]);
assertEquals("Connection", _hdr[1]);
assertEquals("close", _val[1]);
assertEquals(1, _headers);
}
@Before
public void init()
{
@ -1429,9 +1486,10 @@ public class HttpParserTest
private boolean _headerCompleted;
private boolean _messageCompleted;
private class Handler implements HttpParser.RequestHandler<ByteBuffer>, HttpParser.ResponseHandler<ByteBuffer>
private class Handler implements HttpParser.RequestHandler<ByteBuffer>, HttpParser.ResponseHandler<ByteBuffer>, HttpParser.ProxyHandler
{
private HttpFields fields;
String _proxy;
@Override
public boolean content(ByteBuffer ref)
@ -1539,5 +1597,11 @@ public class HttpParserTest
{
return 512;
}
@Override
public void proxied(String protocol, String sAddr, String dAddr, int sPort, int dPort)
{
_proxy="PROXY "+protocol+" "+sAddr+" "+dAddr+" "+sPort+" "+dPort;
}
}
}

View File

@ -68,7 +68,7 @@ import org.eclipse.jetty.util.thread.Scheduler;
* HttpTransport.completed().
*
*/
public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable, HttpParser.ProxyHandler
{
private static final Logger LOG = Log.getLogger(HttpChannel.class);
private static final ThreadLocal<HttpChannel<?>> __currentChannel = new ThreadLocal<>();
@ -477,6 +477,14 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
);
}
@Override
public void proxied(String protocol, String sAddr, String dAddr, int sPort, int dPort)
{
_request.setServerName(sAddr);
_request.setServerPort(dPort);
_request.setRemoteAddr(InetSocketAddress.createUnresolved(sAddr,sPort));
}
@Override
public boolean startRequest(HttpMethod httpMethod, String method, ByteBuffer uri, HttpVersion version)
{
@ -842,4 +850,5 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
}
}
}

View File

@ -575,6 +575,49 @@ public class BufferUtil
return minus ? (-val) : val;
throw new NumberFormatException(toString(buffer));
}
/* ------------------------------------------------------------ */
/**
* Convert buffer to an integer. Parses up to the first non-numeric character. If no number is found an IllegalArgumentException is thrown
*
* @param buffer
* A buffer containing an integer in flush mode. The position is updated.
* @return an int
*/
public static int takeInt(ByteBuffer buffer)
{
int val = 0;
boolean started = false;
boolean minus = false;
int i;
for (i = buffer.position(); i < buffer.limit(); i++)
{
byte b = buffer.get(i);
if (b <= SPACE)
{
if (started)
break;
}
else if (b >= '0' && b <= '9')
{
val = val * 10 + (b - '0');
started = true;
}
else if (b == MINUS && !started)
{
minus = true;
}
else
break;
}
if (started)
{
buffer.position(i);
return minus ? (-val) : val;
}
throw new NumberFormatException(toString(buffer));
}
/**
* Convert buffer to an long. Parses up to the first non-numeric character. If no number is found an IllegalArgumentException is thrown