Issue #845 data rate limits

Initial thoughts
This commit is contained in:
Greg Wilkins 2016-08-16 16:24:14 +10:00
parent 4527ba1801
commit 3d93d39b39
9 changed files with 128 additions and 7 deletions

View File

@ -45,15 +45,19 @@ public class FileServer
// In this example it is the current directory but it can be configured to anything that the jvm has access to.
resource_handler.setDirectoriesListed(true);
resource_handler.setWelcomeFiles(new String[]{ "index.html" });
resource_handler.setResourceBase(".");
resource_handler.setResourceBase("/tmp/docroot");
// Add the ResourceHandler to the server.
GzipHandler gzip = new GzipHandler();
server.setHandler(gzip);
HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[] { resource_handler, new DefaultHandler() });
server.setHandler(handlers);
/*
GzipHandler gzip = new GzipHandler();
server.setHandler(gzip);
gzip.setHandler(handlers);
*/
// Start things up! By using the server.join() the server thread will join with the current thread.
// See "http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Thread.html#join()" for more details.
server.start();

View File

@ -0,0 +1,30 @@
package org.eclipse.jetty.embedded;
import java.io.InputStream;
import java.net.Socket;
public class SlowGet
{
public static void main(String... args) throws Exception
{
try(Socket socket = new Socket("localhost",8080))
{
socket.getOutputStream().write("GET /data.txt HTTP/1.0\r\n\r\n".getBytes());
socket.getOutputStream().flush();
InputStream in = socket.getInputStream();
byte[] headers = new byte[1024];
int len = in.read(headers);
System.err.println("read="+len);
int b=0;
while (b>=0)
{
b = in.read();
if ((++len % 1024)==0)
System.err.println("read="+(++len));
}
}
}
}

View File

@ -18,7 +18,6 @@
package org.eclipse.jetty.http;
import java.io.EOFException;
import java.io.IOException;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;

View File

@ -71,6 +71,18 @@ public class HttpChannelOverHTTP2 extends HttpChannel
return _expect100Continue;
}
@Override
public void setIdleTimeout(long timeoutMs)
{
getStream().setIdleTimeout(timeoutMs);
}
@Override
public long getIdleTimeout()
{
return getStream().getIdleTimeout();
}
public Runnable onRequest(HeadersFrame frame)
{
try

View File

@ -24,6 +24,8 @@ import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.ByteChannel;
import java.nio.channels.SocketChannel;
import java.util.Arrays;
import java.util.stream.Collectors;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.log.Log;
@ -168,6 +170,7 @@ public class ChannelEndPoint extends AbstractEndPoint
@Override
public boolean flush(ByteBuffer... buffers) throws IOException
{
System.err.println("FLUSH: "+Arrays.stream(buffers).map(b->BufferUtil.toDetailString(b)).collect(Collectors.toList()));
long flushed=0;
try
{

View File

@ -80,6 +80,7 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
private final Response _response;
private MetaData.Response _committedMetaData;
private RequestLog _requestLog;
private long _oldIdleTimeout;
/** Bytes written after interception (eg after compression) */
private long _written;
@ -596,6 +597,11 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
if (_configuration.getSendDateHeader() && !fields.contains(HttpHeader.DATE))
fields.put(_connector.getServer().getDateField());
long idleTO=_configuration.getIdleTimeout();
_oldIdleTimeout=getIdleTimeout();
if (idleTO>=0 && _oldIdleTimeout!=idleTO)
setIdleTimeout(idleTO);
_request.setMetaData(request);
if (LOG.isDebugEnabled())
@ -627,6 +633,10 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
if (_requestLog!=null )
_requestLog.log(_request, _response);
long idleTO=_configuration.getIdleTimeout();
if (idleTO>=0 && getIdleTimeout()!=_oldIdleTimeout)
setIdleTimeout(_oldIdleTimeout);
_transport.onCompleted();
}

View File

@ -56,6 +56,7 @@ public class HttpConfiguration
private int _responseHeaderSize=8*1024;
private int _headerCacheSize=512;
private int _securePort;
private long _idleTimeout=-1;
private long _blockingTimeout=-1;
private String _secureScheme = HttpScheme.HTTPS.asString();
private boolean _sendServerVersion = true;
@ -64,6 +65,7 @@ public class HttpConfiguration
private boolean _delayDispatchUntilContent = true;
private boolean _persistentConnectionsEnabled = true;
private int _maxErrorDispatches = 10;
private int _minRequestDataRate;
/* ------------------------------------------------------------ */
/**
@ -113,6 +115,7 @@ public class HttpConfiguration
_headerCacheSize=config._headerCacheSize;
_secureScheme=config._secureScheme;
_securePort=config._securePort;
_idleTimeout=config._idleTimeout;
_blockingTimeout=config._blockingTimeout;
_sendDateHeader=config._sendDateHeader;
_sendServerVersion=config._sendServerVersion;
@ -206,6 +209,31 @@ public class HttpConfiguration
return _persistentConnectionsEnabled;
}
/* ------------------------------------------------------------ */
/** Get the max idle time in ms.
* <p>The max idle time is applied to a HTTP request for IO operations and
* delayed dispatch.
* @return the max idle time in ms or if == 0 implies an infinite timeout, &lt;0
* implies no HTTP channel timeout and the connection timeout is used instead.
*/
public long getIdleTimeout()
{
return _idleTimeout;
}
/* ------------------------------------------------------------ */
/** Set the max idle time in ms.
* <p>The max idle time is applied to a HTTP request for IO operations and
* delayed dispatch.
* @param timeoutMs the max idle time in ms or if == 0 implies an infinite timeout, &lt;0
* implies no HTTP channel timeout and the connection timeout is used instead.
*/
public void setIdleTimeout(long timeoutMs)
{
_idleTimeout=timeoutMs;
}
/* ------------------------------------------------------------ */
/** Get the timeout applied to blocking operations.
* <p>This timeout is in addition to the {@link Connector#getIdleTimeout()}, and applies
@ -479,4 +507,22 @@ public class HttpConfiguration
{
_maxErrorDispatches=max;
}
/* ------------------------------------------------------------ */
/**
* @return The minimum request data rate in bytes per second; or &lt;=0 for no limit
*/
public int getMinRequestDataRate()
{
return _minRequestDataRate;
}
/* ------------------------------------------------------------ */
/**
* @param bytesPerSecond The minimum request data rate in bytes per second; or &lt;=0 for no limit
*/
public void setMinRequestDataRate(int bytesPerSecond)
{
_minRequestDataRate=bytesPerSecond;
}
}

View File

@ -56,6 +56,7 @@ public class HttpInput extends ServletInputStream implements Runnable
private final HttpChannelState _channelState;
private ReadListener _listener;
private State _state = STREAM;
private long _contentArrived;
private long _contentConsumed;
private long _blockingTimeoutAt = -1;
@ -83,6 +84,7 @@ public class HttpInput extends ServletInputStream implements Runnable
}
_listener = null;
_state = STREAM;
_contentArrived = 0;
_contentConsumed = 0;
}
}
@ -139,9 +141,23 @@ public class HttpInput extends ServletInputStream implements Runnable
{
synchronized (_inputQ)
{
long now=System.currentTimeMillis();
if (_blockingTimeoutAt>=0 && !isAsync())
_blockingTimeoutAt=System.currentTimeMillis()+getHttpChannelState().getHttpChannel().getHttpConfiguration().getBlockingTimeout();
_blockingTimeoutAt=now+getHttpChannelState().getHttpChannel().getHttpConfiguration().getBlockingTimeout();
int minRequestDataRate=_channelState.getHttpChannel().getHttpConfiguration().getMinRequestDataRate();
if (minRequestDataRate>0)
{
long period=now-_channelState.getHttpChannel().getRequest().getTimeStamp();
if (period>=1000)
{
long data_rate = _contentArrived / (now-_channelState.getHttpChannel().getRequest().getTimeStamp());
if (data_rate<minRequestDataRate)
throw new IOException(String.format("Request Data rate %d < %d B/s",data_rate,minRequestDataRate));
}
}
while(true)
{
Content item = nextContent();
@ -410,6 +426,7 @@ public class HttpInput extends ServletInputStream implements Runnable
boolean woken=false;
synchronized (_inputQ)
{
_contentArrived+=item.remaining();
_inputQ.offer(item);
if (LOG.isDebugEnabled())
LOG.debug("{} addContent {}", this, item);

View File

@ -546,7 +546,7 @@ public class ResourceHandler extends HandlerWrapper implements ResourceFactory
};
// Can we use a memory mapped file?
if (_minMemoryMappedContentLength>0 &&
if (_minMemoryMappedContentLength>=0 &&
resource.length()>_minMemoryMappedContentLength &&
resource.length()<Integer.MAX_VALUE &&
resource instanceof PathResource)