diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java index 8e89cf2c5b2..b5c8d33e300 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java @@ -141,6 +141,7 @@ public class HTTP2Connection extends AbstractConnection protected class HTTP2Producer implements ExecutionStrategy.Producer { + private final Callback fillCallback = new FillCallback(); private ByteBuffer buffer; @Override @@ -182,7 +183,7 @@ public class HTTP2Connection extends AbstractConnection if (filled == 0) { release(); - fillInterested(); + getEndPoint().fillInterested(fillCallback); return null; } else if (filled < 0) @@ -205,4 +206,19 @@ public class HTTP2Connection extends AbstractConnection } } } + + private class FillCallback implements Callback.NonBlocking + { + @Override + public void succeeded() + { + onFillable(); + } + + @Override + public void failed(Throwable x) + { + onFillInterestedFailed(x); + } + } } diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Session.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Session.java index 2b2c640b6fe..8c4b277175f 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Session.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Session.java @@ -84,6 +84,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio private int maxLocalStreams; private int maxRemoteStreams; private long streamIdleTimeout; + private int initialSessionRecvWindow; private boolean pushEnabled; private long idleTime; @@ -149,6 +150,17 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio this.streamIdleTimeout = streamIdleTimeout; } + @ManagedAttribute("The initial size of session's flow control receive window") + public int getInitialSessionRecvWindow() + { + return initialSessionRecvWindow; + } + + public void setInitialSessionRecvWindow(int initialSessionRecvWindow) + { + this.initialSessionRecvWindow = initialSessionRecvWindow; + } + public EndPoint getEndPoint() { return endPoint; diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java index 0c70d362342..8038b5a77ba 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java @@ -36,6 +36,7 @@ import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.annotation.Name; import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.thread.ExecutionStrategy; +import org.eclipse.jetty.util.thread.strategy.ProduceExecuteConsume; @ManagedObject public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConnectionFactory @@ -43,11 +44,12 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne private final Connection.Listener connectionListener = new ConnectionListener(); private final HttpConfiguration httpConfiguration; private int maxDynamicTableSize = 4096; - private int initialStreamSendWindow = FlowControlStrategy.DEFAULT_WINDOW_SIZE; + private int initialStreamRecvWindow = FlowControlStrategy.DEFAULT_WINDOW_SIZE; + private int initialSessionRecvWindow = FlowControlStrategy.DEFAULT_WINDOW_SIZE; private int maxConcurrentStreams = -1; private int maxHeaderBlockFragment = 0; private FlowControlStrategy.Factory flowControlStrategyFactory = () -> new BufferingFlowControlStrategy(0.5F); - private ExecutionStrategy.Factory executionStrategyFactory = ExecutionStrategy.Factory.getDefault(); + private ExecutionStrategy.Factory executionStrategyFactory = new ProduceExecuteConsume.Factory(); public AbstractHTTP2ServerConnectionFactory(@Name("config") HttpConfiguration httpConfiguration) { @@ -72,15 +74,46 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne this.maxDynamicTableSize = maxDynamicTableSize; } - @ManagedAttribute("The initial size of stream's flow control send window") - public int getInitialStreamSendWindow() + @ManagedAttribute("The initial size of session's flow control receive window") + public int getInitialSessionRecvWindow() { - return initialStreamSendWindow; + return initialSessionRecvWindow; } + public void setInitialSessionRecvWindow(int initialSessionRecvWindow) + { + this.initialSessionRecvWindow = initialSessionRecvWindow; + } + + @ManagedAttribute("The initial size of stream's flow control receive window") + public int getInitialStreamRecvWindow() + { + return initialStreamRecvWindow; + } + + public void setInitialStreamRecvWindow(int initialStreamRecvWindow) + { + this.initialStreamRecvWindow = initialStreamRecvWindow; + } + + /** + * @deprecated use {@link #getInitialStreamRecvWindow()} instead, + * since "send" is meant on the client, but this is the server configuration + */ + @Deprecated + public int getInitialStreamSendWindow() + { + return getInitialStreamRecvWindow(); + } + + /** + * @deprecated use {@link #setInitialStreamRecvWindow(int)} instead, + * since "send" is meant on the client, but this is the server configuration + */ + @Deprecated public void setInitialStreamSendWindow(int initialStreamSendWindow) { - this.initialStreamSendWindow = initialStreamSendWindow; + setInitialStreamRecvWindow(initialStreamSendWindow); } @ManagedAttribute("The max number of concurrent streams per session") @@ -144,6 +177,7 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne // the typical case is that the connection will be busier and the // stream idle timeout will expire earlier that the connection's. session.setStreamIdleTimeout(endPoint.getIdleTimeout()); + session.setInitialSessionRecvWindow(getInitialSessionRecvWindow()); ServerParser parser = newServerParser(connector, session); HTTP2Connection connection = new HTTP2ServerConnection(connector.getByteBufferPool(), connector.getExecutor(), diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnection.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnection.java index f6f0dc8c0a4..72f5c9b3262 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnection.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnection.java @@ -184,7 +184,7 @@ public class HTTP2ServerConnection extends HTTP2Connection implements Connection upgradeFrames.add(new PrefaceFrame()); upgradeFrames.add(settingsFrame); // Remember the request to send a response from onOpen(). - upgradeFrames.add(new HeadersFrame(1, request, null, true)); + upgradeFrames.add(new HeadersFrame(1, new Request(request), null, true)); } return true; } diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java index 89b2abb5222..301431c9980 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java @@ -49,7 +49,7 @@ public class HTTP2ServerConnectionFactory extends AbstractHTTP2ServerConnectionF { super(httpConfiguration); } - + protected HTTP2ServerConnectionFactory(@Name("config") HttpConfiguration httpConfiguration,String... protocols) { super(httpConfiguration,protocols); @@ -93,7 +93,7 @@ public class HTTP2ServerConnectionFactory extends AbstractHTTP2ServerConnectionF { Map settings = new HashMap<>(); settings.put(SettingsFrame.HEADER_TABLE_SIZE, getMaxDynamicTableSize()); - settings.put(SettingsFrame.INITIAL_WINDOW_SIZE, getInitialStreamSendWindow()); + settings.put(SettingsFrame.INITIAL_WINDOW_SIZE, getInitialStreamRecvWindow()); int maxConcurrentStreams = getMaxConcurrentStreams(); if (maxConcurrentStreams >= 0) settings.put(SettingsFrame.MAX_CONCURRENT_STREAMS, maxConcurrentStreams); diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerSession.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerSession.java index 69bf2979f50..0e401f5d544 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerSession.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerSession.java @@ -33,6 +33,7 @@ import org.eclipse.jetty.http2.frames.Frame; import org.eclipse.jetty.http2.frames.HeadersFrame; import org.eclipse.jetty.http2.frames.PushPromiseFrame; import org.eclipse.jetty.http2.frames.SettingsFrame; +import org.eclipse.jetty.http2.frames.WindowUpdateFrame; import org.eclipse.jetty.http2.generator.Generator; import org.eclipse.jetty.http2.parser.ServerParser; import org.eclipse.jetty.io.EndPoint; @@ -60,9 +61,17 @@ public class HTTP2ServerSession extends HTTP2Session implements ServerParser.Lis Map settings = notifyPreface(this); if (settings == null) settings = Collections.emptyMap(); - SettingsFrame frame = new SettingsFrame(settings, false); - // TODO: consider sending a WINDOW_UPDATE to enlarge the session send window of the client. - frames(null, Callback.NOOP, frame, Frame.EMPTY_ARRAY); + SettingsFrame settingsFrame = new SettingsFrame(settings, false); + + WindowUpdateFrame windowFrame = null; + int sessionWindow = getInitialSessionRecvWindow() - FlowControlStrategy.DEFAULT_WINDOW_SIZE; + if (sessionWindow > 0) + windowFrame = new WindowUpdateFrame(0, sessionWindow); + + if (windowFrame == null) + frames(null, Callback.NOOP, settingsFrame, Frame.EMPTY_ARRAY); + else + frames(null, Callback.NOOP, settingsFrame, windowFrame); } @Override diff --git a/jetty-http2/http2-server/src/test/resources/jetty-logging.properties b/jetty-http2/http2-server/src/test/resources/jetty-logging.properties index 13f7aba3862..9611e7c6ad5 100644 --- a/jetty-http2/http2-server/src/test/resources/jetty-logging.properties +++ b/jetty-http2/http2-server/src/test/resources/jetty-logging.properties @@ -1,3 +1,4 @@ org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog -org.eclipse.jetty.http2.hpack.LEVEL=INFO +#org.eclipse.jetty.LEVEL=DEBUG #org.eclipse.jetty.http2.LEVEL=DEBUG +org.eclipse.jetty.http2.hpack.LEVEL=INFO diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ErrorPageErrorHandler.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ErrorPageErrorHandler.java index 3dffd4c3c1e..bafd8241056 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ErrorPageErrorHandler.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ErrorPageErrorHandler.java @@ -41,60 +41,67 @@ public class ErrorPageErrorHandler extends ErrorHandler implements ErrorHandler. { public final static String GLOBAL_ERROR_PAGE = "org.eclipse.jetty.server.error_page.global"; private static final Logger LOG = Log.getLogger(ErrorPageErrorHandler.class); - enum PageLookupTechnique{ THROWABLE, STATUS_CODE, GLOBAL } + + private enum PageLookupTechnique + { + THROWABLE, STATUS_CODE, GLOBAL + } protected ServletContext _servletContext; - private final Map _errorPages= new HashMap<>(); // code or exception to URL - private final List _errorPageList= new ArrayList<>(); // list of ErrorCode by range + private final Map _errorPages = new HashMap<>(); // code or exception to URL + private final List _errorPageList = new ArrayList<>(); // list of ErrorCode by range @Override public String getErrorPage(HttpServletRequest request) { - String error_page= null; + String error_page = null; PageLookupTechnique pageSource = null; - + + Class matchedThrowable = null; Throwable th = (Throwable)request.getAttribute(Dispatcher.ERROR_EXCEPTION); // Walk the cause hierarchy - while (error_page == null && th != null ) + while (error_page == null && th != null) { pageSource = PageLookupTechnique.THROWABLE; - - Class exClass=th.getClass(); + + Class exClass = th.getClass(); error_page = _errorPages.get(exClass.getName()); // walk the inheritance hierarchy while (error_page == null) { - exClass= exClass.getSuperclass(); - if (exClass==null) + exClass = exClass.getSuperclass(); + if (exClass == null) break; - error_page=_errorPages.get(exClass.getName()); + error_page = _errorPages.get(exClass.getName()); } - th=(th instanceof ServletException)?((ServletException)th).getRootCause():null; + if (error_page != null) + matchedThrowable = exClass; + + th = (th instanceof ServletException) ? ((ServletException)th).getRootCause() : null; } Integer errorStatusCode = null; - + if (error_page == null) { pageSource = PageLookupTechnique.STATUS_CODE; - + // look for an exact code match errorStatusCode = (Integer)request.getAttribute(Dispatcher.ERROR_STATUS_CODE); - if (errorStatusCode!=null) + if (errorStatusCode != null) { - error_page= (String)_errorPages.get(Integer.toString(errorStatusCode)); + error_page = _errorPages.get(Integer.toString(errorStatusCode)); // if still not found - if ((error_page == null) && (_errorPageList != null)) + if (error_page == null) { // look for an error code range match. - for (int i = 0; i < _errorPageList.size(); i++) + for (ErrorCodeRange errCode : _errorPageList) { - ErrorCodeRange errCode = _errorPageList.get(i); if (errCode.isInRange(errorStatusCode)) { error_page = errCode.getUri(); @@ -111,7 +118,7 @@ public class ErrorPageErrorHandler extends ErrorHandler implements ErrorHandler. pageSource = PageLookupTechnique.GLOBAL; error_page = _errorPages.get(GLOBAL_ERROR_PAGE); } - + if (LOG.isDebugEnabled()) { StringBuilder dbg = new StringBuilder(); @@ -122,10 +129,13 @@ public class ErrorPageErrorHandler extends ErrorHandler implements ErrorHandler. switch (pageSource) { case THROWABLE: - dbg.append(" (from Throwable "); - dbg.append(th.getClass().getName()); + dbg.append(" (using matched Throwable "); + dbg.append(matchedThrowable.getName()); + dbg.append(" / actually thrown as "); + Throwable originalThrowable = (Throwable)request.getAttribute(Dispatcher.ERROR_EXCEPTION); + dbg.append(originalThrowable.getClass().getName()); dbg.append(')'); - LOG.debug(dbg.toString(),th); + LOG.debug(dbg.toString(), th); break; case STATUS_CODE: dbg.append(" (from status code "); @@ -143,7 +153,7 @@ public class ErrorPageErrorHandler extends ErrorHandler implements ErrorHandler. return error_page; } - public Map getErrorPages() + public Map getErrorPages() { return _errorPages; } @@ -151,10 +161,10 @@ public class ErrorPageErrorHandler extends ErrorHandler implements ErrorHandler. /** * @param errorPages a map of Exception class names or error codes as a string to URI string */ - public void setErrorPages(Map errorPages) + public void setErrorPages(Map errorPages) { _errorPages.clear(); - if (errorPages!=null) + if (errorPages != null) _errorPages.putAll(errorPages); } @@ -164,11 +174,11 @@ public class ErrorPageErrorHandler extends ErrorHandler implements ErrorHandler. * or may be called directly * * @param exception The exception - * @param uri The URI of the error page. + * @param uri The URI of the error page. */ - public void addErrorPage(Class exception,String uri) + public void addErrorPage(Class exception, String uri) { - _errorPages.put(exception.getName(),uri); + _errorPages.put(exception.getName(), uri); } /** @@ -177,11 +187,11 @@ public class ErrorPageErrorHandler extends ErrorHandler implements ErrorHandler. * or may be called directly * * @param exceptionClassName The exception - * @param uri The URI of the error page. + * @param uri The URI of the error page. */ - public void addErrorPage(String exceptionClassName,String uri) + public void addErrorPage(String exceptionClassName, String uri) { - _errorPages.put(exceptionClassName,uri); + _errorPages.put(exceptionClassName, uri); } /** @@ -190,11 +200,11 @@ public class ErrorPageErrorHandler extends ErrorHandler implements ErrorHandler. * or may be called directly. * * @param code The HTTP status code to match - * @param uri The URI of the error page. + * @param uri The URI of the error page. */ - public void addErrorPage(int code,String uri) + public void addErrorPage(int code, String uri) { - _errorPages.put(Integer.toString(code),uri); + _errorPages.put(Integer.toString(code), uri); } /** @@ -202,8 +212,8 @@ public class ErrorPageErrorHandler extends ErrorHandler implements ErrorHandler. * This method is not available from web.xml and must be called directly. * * @param from The lowest matching status code - * @param to The highest matching status code - * @param uri The URI of the error page. + * @param to The highest matching status code + * @param uri The URI of the error page. */ public void addErrorPage(int from, int to, String uri) { @@ -214,7 +224,7 @@ public class ErrorPageErrorHandler extends ErrorHandler implements ErrorHandler. protected void doStart() throws Exception { super.doStart(); - _servletContext=ContextHandler.getCurrentContext(); + _servletContext = ContextHandler.getCurrentContext(); } private static class ErrorCodeRange @@ -224,7 +234,7 @@ public class ErrorPageErrorHandler extends ErrorHandler implements ErrorHandler. private String _uri; ErrorCodeRange(int from, int to, String uri) - throws IllegalArgumentException + throws IllegalArgumentException { if (from > to) throw new IllegalArgumentException("from>to"); @@ -236,7 +246,7 @@ public class ErrorPageErrorHandler extends ErrorHandler implements ErrorHandler. boolean isInRange(int value) { - return (value >= _from) && (value <= _to); + return _from <= value && value <= _to; } String getUri()