Fixes #4258 - RateControl should be per-connection.

Introduced RateControl.Factory to create instances of RateControl
for each connection.
Modified relevant XML files and added distribution test for h2.

Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
Simone Bordet 2019-11-05 18:40:27 +01:00
parent e09444eeb5
commit f8449868e6
6 changed files with 91 additions and 19 deletions

View File

@ -36,4 +36,18 @@ public interface RateControl
* @return true IFF the rate is within limits
*/
public boolean onEvent(Object event);
/**
* Factory to create RateControl instances.
*/
public interface Factory
{
/**
* @return a new RateControl instance.
*/
public default RateControl newRateControl()
{
return NO_RATE_CONTROL;
}
}
}

View File

@ -67,4 +67,20 @@ public class WindowRateControl implements RateControl
events.add(now + window);
return size.incrementAndGet() <= maxEvents;
}
public static class Factory implements RateControl.Factory
{
private final int maxEventRate;
public Factory(int maxEventRate)
{
this.maxEventRate = maxEventRate;
}
@Override
public RateControl newRateControl()
{
return WindowRateControl.fromEventsPerSecond(maxEventRate);
}
}
}

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
<!-- ============================================================= --><!-- Configure an HTTP2 on the ssl connector. --><!-- ============================================================= -->
<Configure id="sslConnector" class="org.eclipse.jetty.server.ServerConnector">
<Call name="addConnectionFactory">
<Arg>
@ -10,10 +10,10 @@
<Set name="initialStreamRecvWindow"><Property name="jetty.http2.initialStreamRecvWindow" default="524288"/></Set>
<Set name="initialSessionRecvWindow"><Property name="jetty.http2.initialSessionRecvWindow" default="1048576"/></Set>
<Set name="maxSettingsKeys"><Property name="jetty.http2.maxSettingsKeys" default="64"/></Set>
<Set name="rateControl">
<Call class="org.eclipse.jetty.http2.parser.WindowRateControl" name="fromEventsPerSecond">
<Set name="rateControlFactory">
<New class="org.eclipse.jetty.http2.parser.WindowRateControl$Factory">
<Arg type="int"><Property name="jetty.http2.rateControl.maxEventsPerSecond" default="20"/></Arg>
</Call>
</New>
</Set>
</New>
</Arg>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
<!-- ============================================================= --><!-- Configure an HTTP2 on the ssl connector. --><!-- ============================================================= -->
<Configure id="httpConnector" class="org.eclipse.jetty.server.ServerConnector">
<Call name="addConnectionFactory">
<Arg>
@ -9,10 +9,10 @@
<Set name="maxConcurrentStreams"><Property name="jetty.http2c.maxConcurrentStreams" deprecated="http2.maxConcurrentStreams" default="1024"/></Set>
<Set name="initialStreamRecvWindow"><Property name="jetty.http2c.initialStreamRecvWindow" default="65535"/></Set>
<Set name="maxSettingsKeys"><Property name="jetty.http2.maxSettingsKeys" default="64"/></Set>
<Set name="rateControl">
<Call class="org.eclipse.jetty.http2.parser.WindowRateControl" name="fromEventsPerSecond">
<Set name="rateControlFactory">
<New class="org.eclipse.jetty.http2.parser.WindowRateControl$Factory">
<Arg type="int"><Property name="jetty.http2.rateControl.maxEventsPerSecond" default="20"/></Arg>
</Call>
</New>
</Set>
</New>
</Arg>

View File

@ -19,7 +19,6 @@
package org.eclipse.jetty.http2.server;
import java.io.IOException;
import java.time.Duration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@ -61,7 +60,7 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne
private int maxHeaderBlockFragment = 0;
private int maxFrameLength = Frame.DEFAULT_MAX_LENGTH;
private int maxSettingsKeys = SettingsFrame.DEFAULT_MAX_KEYS;
private RateControl rateControl = new WindowRateControl(20, Duration.ofSeconds(1));
private RateControl.Factory rateControlFactory = new WindowRateControl.Factory(20);
private FlowControlStrategy.Factory flowControlStrategyFactory = () -> new BufferingFlowControlStrategy(0.5F);
private long streamIdleTimeout;
@ -182,14 +181,43 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne
this.maxSettingsKeys = maxSettingsKeys;
}
/**
* @return null
* @deprecated use {@link #getRateControlFactory()} instead
*/
@Deprecated
public RateControl getRateControl()
{
return rateControl;
return null;
}
/**
* @param rateControl ignored
* @throws UnsupportedOperationException when invoked
* @deprecated use {@link #setRateControlFactory(RateControl.Factory)} instead
*/
@Deprecated
public void setRateControl(RateControl rateControl)
{
this.rateControl = rateControl;
throw new UnsupportedOperationException();
}
/**
* @return the factory that creates RateControl objects
*/
public RateControl.Factory getRateControlFactory()
{
return rateControlFactory;
}
/**
* <p>Sets the factory that creates a per-connection RateControl object.</p>
*
* @param rateControlFactory the factory that creates RateControl objects
*/
public void setRateControlFactory(RateControl.Factory rateControlFactory)
{
this.rateControlFactory = Objects.requireNonNull(rateControlFactory);
}
/**
@ -251,7 +279,7 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne
session.setInitialSessionRecvWindow(getInitialSessionRecvWindow());
session.setWriteThreshold(getHttpConfiguration().getOutputBufferSize());
ServerParser parser = newServerParser(connector, session, getRateControl());
ServerParser parser = newServerParser(connector, session, getRateControlFactory().newRateControl());
parser.setMaxFrameLength(getMaxFrameLength());
parser.setMaxSettingsKeys(getMaxSettingsKeys());

View File

@ -33,6 +33,7 @@ import org.eclipse.jetty.unixsocket.UnixSocketConnector;
import org.eclipse.jetty.unixsocket.client.HttpClientTransportOverUnixSockets;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledOnJre;
import org.junit.jupiter.api.condition.JRE;
@ -155,6 +156,18 @@ public class DistributionTests extends AbstractDistributionTest
@Test
@DisabledOnJre(JRE.JAVA_8)
public void testSimpleWebAppWithJSPOverH2C() throws Exception
{
testSimpleWebAppWithJSPOverHTTP2(false);
}
@Test
@DisabledOnJre(JRE.JAVA_8)
public void testSimpleWebAppWithJSPOverH2() throws Exception
{
testSimpleWebAppWithJSPOverHTTP2(true);
}
private void testSimpleWebAppWithJSPOverHTTP2(boolean ssl) throws Exception
{
String jettyVersion = System.getProperty("jettyVersion");
DistributionTester distribution = DistributionTester.Builder.newInstance()
@ -164,7 +177,7 @@ public class DistributionTests extends AbstractDistributionTest
String[] args1 = {
"--create-startd",
"--add-to-start=http2c,jsp,deploy"
"--add-to-start=jsp,deploy," + (ssl ? "http2" : "http2c")
};
try (DistributionTester.Run run1 = distribution.start(args1))
{
@ -175,13 +188,14 @@ public class DistributionTests extends AbstractDistributionTest
distribution.installWarFile(war, "test");
int port = distribution.freePort();
try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port))
String portProp = ssl ? "jetty.ssl.port" : "jetty.http.port";
try (DistributionTester.Run run2 = distribution.start(portProp + "=" + port))
{
assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS));
HTTP2Client h2Client = new HTTP2Client();
startHttpClient(() -> new HttpClient(new HttpClientTransportOverHTTP2(h2Client), null));
ContentResponse response = client.GET("http://localhost:" + port + "/test/index.jsp");
startHttpClient(() -> new HttpClient(new HttpClientTransportOverHTTP2(h2Client), new SslContextFactory.Client(true)));
ContentResponse response = client.GET((ssl ? "https" : "http") + "://localhost:" + port + "/test/index.jsp");
assertEquals(HttpStatus.OK_200, response.getStatus());
assertThat(response.getContentAsString(), containsString("Hello"));
assertThat(response.getContentAsString(), not(containsString("<%")));