Implemented the PROXY protocol
Moved the PROXY protocol support from HttpParser to a ConnectionFactory. See http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt
This commit is contained in:
parent
59fc60972e
commit
a308c087ed
|
@ -632,28 +632,8 @@ 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 (_method==HttpMethod.PROXY)
|
||||
{
|
||||
if (!(_requestHandler instanceof ProxyHandler))
|
||||
throw new BadMessageException();
|
||||
|
||||
String protocol=_uri.toString();
|
||||
// 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
|
||||
|
||||
if (version!=null)
|
||||
{
|
||||
int pos = buffer.position()+version.asString().length()-1;
|
||||
if (pos<buffer.limit())
|
||||
|
@ -1574,14 +1554,6 @@ public class HttpParser
|
|||
public int getHeaderCacheSize();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
public interface ProxyHandler
|
||||
{
|
||||
void proxied(String protocol, String sAddr, String dAddr, int sPort, int dPort);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
|
|
|
@ -1633,7 +1633,7 @@ public class HttpParserTest
|
|||
private boolean _headerCompleted;
|
||||
private boolean _messageCompleted;
|
||||
|
||||
private class Handler implements HttpParser.RequestHandler, HttpParser.ResponseHandler, HttpParser.ProxyHandler
|
||||
private class Handler implements HttpParser.RequestHandler, HttpParser.ResponseHandler
|
||||
{
|
||||
private HttpFields fields;
|
||||
String _proxy;
|
||||
|
@ -1742,11 +1742,5 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
/**
|
||||
* A HttpChannel customized to be transported over the HTTP/1 protocol
|
||||
*/
|
||||
class HttpChannelOverHttp extends HttpChannel implements HttpParser.RequestHandler, HttpParser.ProxyHandler
|
||||
class HttpChannelOverHttp extends HttpChannel implements HttpParser.RequestHandler
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(HttpChannelOverHttp.class);
|
||||
|
||||
|
@ -99,16 +99,6 @@ class HttpChannelOverHttp extends HttpChannel implements HttpParser.RequestHandl
|
|||
_expect102Processing = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void proxied(String protocol, String sAddr, String dAddr, int sPort, int dPort)
|
||||
{
|
||||
_metadata.setMethod(HttpMethod.CONNECT.asString());
|
||||
Request request = getRequest();
|
||||
request.setAttribute("PROXY", protocol);
|
||||
request.setAuthority(sAddr,dPort);
|
||||
request.setRemoteAddr(InetSocketAddress.createUnresolved(sAddr,sPort));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parsedHeader(HttpField field)
|
||||
|
|
|
@ -0,0 +1,306 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.ReadPendingException;
|
||||
import java.nio.channels.WritePendingException;
|
||||
|
||||
import org.eclipse.jetty.io.AbstractConnection;
|
||||
import org.eclipse.jetty.io.Connection;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** ConnectionFactory for the PROXY Protocol.
|
||||
* <p>This factory can be placed in front of any other connection factory
|
||||
* to process the proxy line before the normal protocol handling</p>
|
||||
*
|
||||
* @see http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt
|
||||
*/
|
||||
public class ProxyConnectionFactory extends AbstractConnectionFactory
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(ProxyConnectionFactory.class);
|
||||
private final String _next;
|
||||
|
||||
public ProxyConnectionFactory(String nextProtocol)
|
||||
{
|
||||
super("haproxy");
|
||||
_next=nextProtocol;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection newConnection(Connector connector, EndPoint endp)
|
||||
{
|
||||
return new ProxyConnection(endp,connector,_next);
|
||||
}
|
||||
|
||||
public static class ProxyConnection extends AbstractConnection
|
||||
{
|
||||
// 0 1 2 3 4 5 6
|
||||
// 98765432109876543210987654321
|
||||
// PROXY P R.R.R.R L.L.L.L R Lrn
|
||||
|
||||
private final int[] __size = {29,23,21,13,5,3,1};
|
||||
private final Connector _connector;
|
||||
private final String _next;
|
||||
private final StringBuilder _builder=new StringBuilder();
|
||||
private final String[] _field=new String[6];
|
||||
private int _fields;
|
||||
private int _length;
|
||||
|
||||
protected ProxyConnection(EndPoint endp, Connector connector, String next)
|
||||
{
|
||||
super(endp,connector.getExecutor(),false);
|
||||
_connector=connector;
|
||||
_next=next;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen()
|
||||
{
|
||||
super.onOpen();
|
||||
fillInterested();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFillable()
|
||||
{
|
||||
try
|
||||
{
|
||||
ByteBuffer buffer=null;
|
||||
loop: while(true)
|
||||
{
|
||||
// Create a buffer that will not read too much data
|
||||
int size=Math.max(1,__size[_fields]-_builder.length());
|
||||
if (buffer==null || buffer.capacity()!=size)
|
||||
buffer=BufferUtil.allocate(size);
|
||||
else
|
||||
BufferUtil.clear(buffer);
|
||||
|
||||
// Read data
|
||||
int fill=getEndPoint().fill(buffer);
|
||||
if (fill<0)
|
||||
{
|
||||
getEndPoint().shutdownOutput();
|
||||
return;
|
||||
}
|
||||
if (fill==0)
|
||||
{
|
||||
fillInterested();
|
||||
return;
|
||||
}
|
||||
|
||||
_length+=fill;
|
||||
if (_length>=108)
|
||||
{
|
||||
LOG.warn("PROXY line too long {}",getEndPoint());
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
// parse fields
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
byte b = buffer.get();
|
||||
if (_fields<6)
|
||||
{
|
||||
if (b==' ' || b=='\r' && _fields==5)
|
||||
{
|
||||
_field[_fields++]=_builder.toString();
|
||||
_builder.setLength(0);
|
||||
}
|
||||
else if (b<' ')
|
||||
{
|
||||
LOG.warn("Bad char {}",getEndPoint());
|
||||
close();
|
||||
return;
|
||||
}
|
||||
else
|
||||
_builder.append((char)b);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (b=='\n')
|
||||
break loop;
|
||||
|
||||
LOG.warn("Bad CRLF {}",getEndPoint());
|
||||
close();
|
||||
return;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check proxy
|
||||
if (!"PROXY".equals(_field[0]))
|
||||
{
|
||||
LOG.warn("Bad PROXY {}",getEndPoint());
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract Addresses
|
||||
InetSocketAddress remote=new InetSocketAddress(_field[2],Integer.parseInt(_field[4]));
|
||||
InetSocketAddress local =new InetSocketAddress(_field[3],Integer.parseInt(_field[5]));
|
||||
|
||||
// Create the next protocol
|
||||
ConnectionFactory connectionFactory = _connector.getConnectionFactory(_next);
|
||||
if (connectionFactory == null)
|
||||
{
|
||||
LOG.info("{} next protocol '{}'",getEndPoint(), _next);
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
EndPoint endPoint = new ProxyEndPoint(getEndPoint(),remote,local);
|
||||
Connection oldConnection = getEndPoint().getConnection();
|
||||
Connection newConnection = connectionFactory.newConnection(_connector, endPoint);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Switching to {} {}", _next, getEndPoint());
|
||||
|
||||
oldConnection.onClose();
|
||||
endPoint.setConnection(newConnection);
|
||||
newConnection.onOpen();
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
LOG.warn("Bad PROXY {} {}",e.toString(),getEndPoint());
|
||||
LOG.debug(e);
|
||||
close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class ProxyEndPoint implements EndPoint
|
||||
{
|
||||
private final EndPoint _endp;
|
||||
private final InetSocketAddress _remote;
|
||||
private final InetSocketAddress _local;
|
||||
|
||||
public ProxyEndPoint(EndPoint endp, InetSocketAddress remote, InetSocketAddress local)
|
||||
{
|
||||
_endp=endp;
|
||||
_remote=remote;
|
||||
_local=local;
|
||||
}
|
||||
|
||||
public InetSocketAddress getLocalAddress()
|
||||
{
|
||||
return _local;
|
||||
}
|
||||
|
||||
public InetSocketAddress getRemoteAddress()
|
||||
{
|
||||
return _remote;
|
||||
}
|
||||
|
||||
public boolean isOpen()
|
||||
{
|
||||
return _endp.isOpen();
|
||||
}
|
||||
|
||||
public long getCreatedTimeStamp()
|
||||
{
|
||||
return _endp.getCreatedTimeStamp();
|
||||
}
|
||||
|
||||
public void shutdownOutput()
|
||||
{
|
||||
_endp.shutdownOutput();
|
||||
}
|
||||
|
||||
public boolean isOutputShutdown()
|
||||
{
|
||||
return _endp.isOutputShutdown();
|
||||
}
|
||||
|
||||
public boolean isInputShutdown()
|
||||
{
|
||||
return _endp.isInputShutdown();
|
||||
}
|
||||
|
||||
public void close()
|
||||
{
|
||||
_endp.close();
|
||||
}
|
||||
|
||||
public int fill(ByteBuffer buffer) throws IOException
|
||||
{
|
||||
return _endp.fill(buffer);
|
||||
}
|
||||
|
||||
public boolean flush(ByteBuffer... buffer) throws IOException
|
||||
{
|
||||
return _endp.flush(buffer);
|
||||
}
|
||||
|
||||
public Object getTransport()
|
||||
{
|
||||
return _endp.getTransport();
|
||||
}
|
||||
|
||||
public long getIdleTimeout()
|
||||
{
|
||||
return _endp.getIdleTimeout();
|
||||
}
|
||||
|
||||
public void setIdleTimeout(long idleTimeout)
|
||||
{
|
||||
_endp.setIdleTimeout(idleTimeout);
|
||||
}
|
||||
|
||||
public void fillInterested(Callback callback) throws ReadPendingException
|
||||
{
|
||||
_endp.fillInterested(callback);
|
||||
}
|
||||
|
||||
public void write(Callback callback, ByteBuffer... buffers) throws WritePendingException
|
||||
{
|
||||
_endp.write(callback,buffers);
|
||||
}
|
||||
|
||||
public Connection getConnection()
|
||||
{
|
||||
return _endp.getConnection();
|
||||
}
|
||||
|
||||
public void setConnection(Connection connection)
|
||||
{
|
||||
_endp.setConnection(connection);
|
||||
}
|
||||
|
||||
public void onOpen()
|
||||
{
|
||||
_endp.onOpen();
|
||||
}
|
||||
|
||||
public void onClose()
|
||||
{
|
||||
_endp.onClose();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -103,6 +103,8 @@ public class DumpHandler extends AbstractHandler
|
|||
writer.write("<pre>\ncontentType="+request.getContentType()+"\n</pre>\n");
|
||||
writer.write("<pre>\nencoding="+request.getCharacterEncoding()+"\n</pre>\n");
|
||||
writer.write("<pre>\nservername="+request.getServerName()+"\n</pre>\n");
|
||||
writer.write("<pre>\nlocal="+request.getLocalAddr()+":"+request.getLocalPort()+"\n</pre>\n");
|
||||
writer.write("<pre>\nremote="+request.getRemoteAddr()+":"+request.getRemotePort()+"\n</pre>\n");
|
||||
writer.write("<h3>Header:</h3><pre>");
|
||||
writer.write(request.getMethod()+" "+request.getRequestURI()+" "+request.getProtocol()+"\n");
|
||||
Enumeration<String> headers = request.getHeaderNames();
|
||||
|
|
|
@ -0,0 +1,171 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.server;
|
||||
|
||||
import org.eclipse.jetty.server.handler.ErrorHandler;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ProxyConnectionTest
|
||||
{
|
||||
private Server _server;
|
||||
private LocalConnector _connector;
|
||||
|
||||
@Before
|
||||
public void init() throws Exception
|
||||
{
|
||||
_server = new Server();
|
||||
|
||||
|
||||
HttpConnectionFactory http = new HttpConnectionFactory();
|
||||
http.getHttpConfiguration().setRequestHeaderSize(1024);
|
||||
http.getHttpConfiguration().setResponseHeaderSize(1024);
|
||||
|
||||
ProxyConnectionFactory proxy = new ProxyConnectionFactory(http.getProtocol());
|
||||
|
||||
_connector = new LocalConnector(_server,null,null,null,1,proxy,http);
|
||||
_connector.setIdleTimeout(1000);
|
||||
_server.addConnector(_connector);
|
||||
_server.setHandler(new DumpHandler());
|
||||
ErrorHandler eh=new ErrorHandler();
|
||||
eh.setServer(_server);
|
||||
_server.addBean(eh);
|
||||
_server.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void destroy() throws Exception
|
||||
{
|
||||
_server.stop();
|
||||
_server.join();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimple() throws Exception
|
||||
{
|
||||
String response=_connector.getResponses("PROXY TCP 1.2.3.4 5.6.7.8 111 222\r\n"+
|
||||
"GET /path HTTP/1.1\n"+
|
||||
"Host: server:80\n"+
|
||||
"Connection: close\n"+
|
||||
"\n");
|
||||
|
||||
Assert.assertThat(response,Matchers.containsString("HTTP/1.1 200"));
|
||||
Assert.assertThat(response,Matchers.containsString("pathInfo=/path"));
|
||||
Assert.assertThat(response,Matchers.containsString("local=5.6.7.8:222"));
|
||||
Assert.assertThat(response,Matchers.containsString("remote=1.2.3.4:111"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIPv6() throws Exception
|
||||
{
|
||||
String response=_connector.getResponses("PROXY UNKNOWN eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 65535\r\n"+
|
||||
"GET /path HTTP/1.1\n"+
|
||||
"Host: server:80\n"+
|
||||
"Connection: close\n"+
|
||||
"\n");
|
||||
|
||||
Assert.assertThat(response,Matchers.containsString("HTTP/1.1 200"));
|
||||
Assert.assertThat(response,Matchers.containsString("pathInfo=/path"));
|
||||
Assert.assertThat(response,Matchers.containsString("remote=eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee:65535"));
|
||||
Assert.assertThat(response,Matchers.containsString("local=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:65535"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTooLong() throws Exception
|
||||
{
|
||||
String response=_connector.getResponses("PROXY TOOLONG!!! eeee:eeee:eeee:eeee:0000:0000:0000:0000 ffff:ffff:ffff:ffff:0000:0000:0000:0000 65535 65535\r\n"+
|
||||
"GET /path HTTP/1.1\n"+
|
||||
"Host: server:80\n"+
|
||||
"Connection: close\n"+
|
||||
"\n");
|
||||
|
||||
Assert.assertThat(response,Matchers.equalTo(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotComplete() throws Exception
|
||||
{
|
||||
_connector.setIdleTimeout(100);
|
||||
String response=_connector.getResponses("PROXY TIMEOUT");
|
||||
Assert.assertThat(response,Matchers.equalTo(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadChar() throws Exception
|
||||
{
|
||||
String response=_connector.getResponses("PROXY\tTCP 1.2.3.4 5.6.7.8 111 222\r\n"+
|
||||
"GET /path HTTP/1.1\n"+
|
||||
"Host: server:80\n"+
|
||||
"Connection: close\n"+
|
||||
"\n");
|
||||
Assert.assertThat(response,Matchers.equalTo(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadCRLF() throws Exception
|
||||
{
|
||||
String response=_connector.getResponses("PROXY TCP 1.2.3.4 5.6.7.8 111 222\r \n"+
|
||||
"GET /path HTTP/1.1\n"+
|
||||
"Host: server:80\n"+
|
||||
"Connection: close\n"+
|
||||
"\n");
|
||||
Assert.assertThat(response,Matchers.equalTo(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadPort() throws Exception
|
||||
{
|
||||
String response=_connector.getResponses("PROXY TCP 1.2.3.4 5.6.7.8 9999999999999 222\r\n"+
|
||||
"GET /path HTTP/1.1\n"+
|
||||
"Host: server:80\n"+
|
||||
"Connection: close\n"+
|
||||
"\n");
|
||||
Assert.assertThat(response,Matchers.equalTo(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMissingField() throws Exception
|
||||
{
|
||||
String response=_connector.getResponses("PROXY TCP 1.2.3.4 5.6.7.8 222\r\n"+
|
||||
"GET /path HTTP/1.1\n"+
|
||||
"Host: server:80\n"+
|
||||
"Connection: close\n"+
|
||||
"\n");
|
||||
Assert.assertThat(response,Matchers.equalTo(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHTTP() throws Exception
|
||||
{
|
||||
String response=_connector.getResponses(
|
||||
"GET /path HTTP/1.1\n"+
|
||||
"Host: server:80\n"+
|
||||
"Connection: close\n"+
|
||||
"\n");
|
||||
Assert.assertThat(response,Matchers.equalTo(""));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -516,10 +516,12 @@ public class BufferUtil
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Convert a partial buffer to a String
|
||||
* @param buffer The buffer to convert in flush mode. The buffer is unchanged
|
||||
/** Convert a partial buffer to a String.
|
||||
*
|
||||
* @param position The position in the buffer to start the string from
|
||||
* @param length The length of the buffer
|
||||
* @param charset The {@link Charset} to use to convert the bytes
|
||||
* @return The buffer as a string.
|
||||
* @return The buffer as a string.
|
||||
*/
|
||||
public static String toString(ByteBuffer buffer, int position, int length, Charset charset)
|
||||
{
|
||||
|
@ -547,12 +549,30 @@ public class BufferUtil
|
|||
* @return an int
|
||||
*/
|
||||
public static int toInt(ByteBuffer buffer)
|
||||
{
|
||||
return toInt(buffer,buffer.position(),buffer.remaining());
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* 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 not changed.
|
||||
* @return an int
|
||||
*/
|
||||
public static int toInt(ByteBuffer buffer, int position, int length)
|
||||
{
|
||||
int val = 0;
|
||||
boolean started = false;
|
||||
boolean minus = false;
|
||||
|
||||
for (int i = buffer.position(); i < buffer.limit(); i++)
|
||||
int limit = position+length;
|
||||
|
||||
if (length<=0)
|
||||
throw new NumberFormatException(toString(buffer,position,length,StandardCharsets.UTF_8));
|
||||
|
||||
for (int i = position; i < limit; i++)
|
||||
{
|
||||
byte b = buffer.get(i);
|
||||
if (b <= SPACE)
|
||||
|
|
Loading…
Reference in New Issue