diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServer.java index 2da99b2aeab..cdd1d63c5b5 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServer.java @@ -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(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SlowGet.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SlowGet.java new file mode 100644 index 00000000000..e174dabf99d --- /dev/null +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SlowGet.java @@ -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)); + } + } + } +} diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java index f743379eb4b..b03133a96b4 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java @@ -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; diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java index b70b65bb402..f2ac9c918b7 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java @@ -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 diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ChannelEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ChannelEndPoint.java index 1952760111d..7ef57077531 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ChannelEndPoint.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ChannelEndPoint.java @@ -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 { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java index 05196e33a6a..88b61a5e0aa 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java @@ -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(); } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java index d2ec7bd0398..8d1026484b6 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java @@ -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. + *

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, <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. + *

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, <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. *

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 <=0 for no limit + */ + public int getMinRequestDataRate() + { + return _minRequestDataRate; + } + + /* ------------------------------------------------------------ */ + /** + * @param bytesPerSecond The minimum request data rate in bytes per second; or <=0 for no limit + */ + public void setMinRequestDataRate(int bytesPerSecond) + { + _minRequestDataRate=bytesPerSecond; + } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java index 98b4c4f5431..146c75039bc 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java @@ -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_rate0 && + if (_minMemoryMappedContentLength>=0 && resource.length()>_minMemoryMappedContentLength && resource.length()