HTTP/2 improvements. (#9749)
* Implemented a few required error handlings. * Changed `Parser.init()` to directly take the listener, rather than wrapping it. The reason for this change was to be able to reconfigure the Parser upon receiving a SETTINGS frame. * Initially setting the encoder and decoder max table capacity at the default of 4096, as per spec. Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
parent
debb124dc9
commit
420ec7cc1d
|
@ -149,6 +149,7 @@ public class HttpClient extends ContainerLifeCycle
|
|||
private String defaultRequestContentType = "application/octet-stream";
|
||||
private boolean useInputDirectByteBuffers = true;
|
||||
private boolean useOutputDirectByteBuffers = true;
|
||||
private int maxResponseHeadersSize = -1;
|
||||
private Sweeper destinationSweeper;
|
||||
|
||||
/**
|
||||
|
@ -1181,6 +1182,23 @@ public class HttpClient extends ContainerLifeCycle
|
|||
this.useOutputDirectByteBuffers = useOutputDirectByteBuffers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the max size in bytes of the response headers
|
||||
*/
|
||||
@ManagedAttribute("The max size in bytes of the response headers")
|
||||
public int getMaxResponseHeadersSize()
|
||||
{
|
||||
return maxResponseHeadersSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param maxResponseHeadersSize the max size in bytes of the response headers
|
||||
*/
|
||||
public void setMaxResponseHeadersSize(int maxResponseHeadersSize)
|
||||
{
|
||||
this.maxResponseHeadersSize = maxResponseHeadersSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the forward proxy configuration
|
||||
*/
|
||||
|
|
|
@ -55,7 +55,7 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
|
|||
{
|
||||
super(channel);
|
||||
HttpClient httpClient = channel.getHttpDestination().getHttpClient();
|
||||
parser = new HttpParser(this, -1, httpClient.getHttpCompliance());
|
||||
parser = new HttpParser(this, httpClient.getMaxResponseHeadersSize(), httpClient.getHttpCompliance());
|
||||
HttpClientTransport transport = httpClient.getTransport();
|
||||
if (transport instanceof HttpClientTransportOverHTTP)
|
||||
{
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.eclipse.jetty.http2.FlowControlStrategy;
|
|||
import org.eclipse.jetty.http2.api.Session;
|
||||
import org.eclipse.jetty.http2.frames.Frame;
|
||||
import org.eclipse.jetty.http2.frames.SettingsFrame;
|
||||
import org.eclipse.jetty.http2.hpack.HpackContext;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.ClientConnectionFactory;
|
||||
import org.eclipse.jetty.io.ClientConnector;
|
||||
|
@ -106,11 +107,13 @@ public class HTTP2Client extends ContainerLifeCycle
|
|||
private List<String> protocols = List.of("h2");
|
||||
private int initialSessionRecvWindow = 16 * 1024 * 1024;
|
||||
private int initialStreamRecvWindow = 8 * 1024 * 1024;
|
||||
private int maxFrameLength = Frame.DEFAULT_MAX_LENGTH;
|
||||
private int maxFrameSize = Frame.DEFAULT_MAX_LENGTH;
|
||||
private int maxConcurrentPushedStreams = 32;
|
||||
private int maxSettingsKeys = SettingsFrame.DEFAULT_MAX_KEYS;
|
||||
private int maxDynamicTableSize = 4096;
|
||||
private int maxDecoderTableCapacity = HpackContext.DEFAULT_MAX_TABLE_CAPACITY;
|
||||
private int maxEncoderTableCapacity = HpackContext.DEFAULT_MAX_TABLE_CAPACITY;
|
||||
private int maxHeaderBlockFragment = 0;
|
||||
private int maxResponseHeadersSize = -1;
|
||||
private FlowControlStrategy.Factory flowControlStrategyFactory = () -> new BufferingFlowControlStrategy(0.5F);
|
||||
private long streamIdleTimeout;
|
||||
private boolean useInputDirectByteBuffers = true;
|
||||
|
@ -282,15 +285,27 @@ public class HTTP2Client extends ContainerLifeCycle
|
|||
this.initialStreamRecvWindow = initialStreamRecvWindow;
|
||||
}
|
||||
|
||||
@ManagedAttribute("The max frame length in bytes")
|
||||
@Deprecated
|
||||
public int getMaxFrameLength()
|
||||
{
|
||||
return maxFrameLength;
|
||||
return getMaxFrameSize();
|
||||
}
|
||||
|
||||
public void setMaxFrameLength(int maxFrameLength)
|
||||
@Deprecated
|
||||
public void setMaxFrameLength(int maxFrameSize)
|
||||
{
|
||||
this.maxFrameLength = maxFrameLength;
|
||||
setMaxFrameSize(maxFrameSize);
|
||||
}
|
||||
|
||||
@ManagedAttribute("The max frame size in bytes")
|
||||
public int getMaxFrameSize()
|
||||
{
|
||||
return maxFrameSize;
|
||||
}
|
||||
|
||||
public void setMaxFrameSize(int maxFrameSize)
|
||||
{
|
||||
this.maxFrameSize = maxFrameSize;
|
||||
}
|
||||
|
||||
@ManagedAttribute("The max number of concurrent pushed streams")
|
||||
|
@ -315,15 +330,44 @@ public class HTTP2Client extends ContainerLifeCycle
|
|||
this.maxSettingsKeys = maxSettingsKeys;
|
||||
}
|
||||
|
||||
@ManagedAttribute("The HPACK dynamic table maximum size")
|
||||
public int getMaxDynamicTableSize()
|
||||
@ManagedAttribute("The HPACK encoder dynamic table maximum capacity")
|
||||
public int getMaxEncoderTableCapacity()
|
||||
{
|
||||
return maxDynamicTableSize;
|
||||
return maxEncoderTableCapacity;
|
||||
}
|
||||
|
||||
public void setMaxDynamicTableSize(int maxDynamicTableSize)
|
||||
/**
|
||||
* <p>Sets the limit for the encoder HPACK dynamic table capacity.</p>
|
||||
* <p>Setting this value to {@code 0} disables the use of the dynamic table.</p>
|
||||
*
|
||||
* @param maxEncoderTableCapacity The HPACK encoder dynamic table maximum capacity
|
||||
*/
|
||||
public void setMaxEncoderTableCapacity(int maxEncoderTableCapacity)
|
||||
{
|
||||
this.maxDynamicTableSize = maxDynamicTableSize;
|
||||
this.maxEncoderTableCapacity = maxEncoderTableCapacity;
|
||||
}
|
||||
|
||||
@ManagedAttribute("The HPACK decoder dynamic table maximum capacity")
|
||||
public int getMaxDecoderTableCapacity()
|
||||
{
|
||||
return maxDecoderTableCapacity;
|
||||
}
|
||||
|
||||
public void setMaxDecoderTableCapacity(int maxDecoderTableCapacity)
|
||||
{
|
||||
this.maxDecoderTableCapacity = maxDecoderTableCapacity;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public int getMaxDynamicTableSize()
|
||||
{
|
||||
return getMaxDecoderTableCapacity();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setMaxDynamicTableSize(int maxTableSize)
|
||||
{
|
||||
setMaxDecoderTableCapacity(maxTableSize);
|
||||
}
|
||||
|
||||
@ManagedAttribute("The max size of header block fragments")
|
||||
|
@ -337,6 +381,17 @@ public class HTTP2Client extends ContainerLifeCycle
|
|||
this.maxHeaderBlockFragment = maxHeaderBlockFragment;
|
||||
}
|
||||
|
||||
@ManagedAttribute("The max size of response headers")
|
||||
public int getMaxResponseHeadersSize()
|
||||
{
|
||||
return maxResponseHeadersSize;
|
||||
}
|
||||
|
||||
public void setMaxResponseHeadersSize(int maxResponseHeadersSize)
|
||||
{
|
||||
this.maxResponseHeadersSize = maxResponseHeadersSize;
|
||||
}
|
||||
|
||||
@ManagedAttribute("Whether to use direct ByteBuffers for reading")
|
||||
public boolean isUseInputDirectByteBuffers()
|
||||
{
|
||||
|
|
|
@ -22,10 +22,12 @@ import org.eclipse.jetty.http2.FlowControlStrategy;
|
|||
import org.eclipse.jetty.http2.HTTP2Connection;
|
||||
import org.eclipse.jetty.http2.ISession;
|
||||
import org.eclipse.jetty.http2.api.Session;
|
||||
import org.eclipse.jetty.http2.frames.Frame;
|
||||
import org.eclipse.jetty.http2.frames.PrefaceFrame;
|
||||
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.hpack.HpackContext;
|
||||
import org.eclipse.jetty.http2.parser.Parser;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.ClientConnectionFactory;
|
||||
|
@ -54,27 +56,30 @@ public class HTTP2ClientConnectionFactory implements ClientConnectionFactory
|
|||
Scheduler scheduler = client.getScheduler();
|
||||
Session.Listener listener = (Session.Listener)context.get(SESSION_LISTENER_CONTEXT_KEY);
|
||||
@SuppressWarnings("unchecked")
|
||||
Promise<Session> promise = (Promise<Session>)context.get(SESSION_PROMISE_CONTEXT_KEY);
|
||||
Promise<Session> sessionPromise = (Promise<Session>)context.get(SESSION_PROMISE_CONTEXT_KEY);
|
||||
|
||||
Generator generator = new Generator(byteBufferPool, client.getMaxDynamicTableSize(), client.getMaxHeaderBlockFragment());
|
||||
Generator generator = new Generator(byteBufferPool, client.isUseOutputDirectByteBuffers(), client.getMaxHeaderBlockFragment());
|
||||
FlowControlStrategy flowControl = client.getFlowControlStrategyFactory().newFlowControlStrategy();
|
||||
HTTP2ClientSession session = new HTTP2ClientSession(scheduler, endPoint, generator, listener, flowControl);
|
||||
|
||||
Parser parser = new Parser(byteBufferPool, client.getMaxResponseHeadersSize());
|
||||
parser.setMaxFrameSize(client.getMaxFrameSize());
|
||||
parser.setMaxSettingsKeys(client.getMaxSettingsKeys());
|
||||
|
||||
HTTP2ClientSession session = new HTTP2ClientSession(scheduler, endPoint, parser, generator, listener, flowControl);
|
||||
session.setMaxRemoteStreams(client.getMaxConcurrentPushedStreams());
|
||||
session.setMaxEncoderTableCapacity(client.getMaxEncoderTableCapacity());
|
||||
long streamIdleTimeout = client.getStreamIdleTimeout();
|
||||
if (streamIdleTimeout > 0)
|
||||
session.setStreamIdleTimeout(streamIdleTimeout);
|
||||
|
||||
Parser parser = new Parser(byteBufferPool, session, 4096, 8192);
|
||||
parser.setMaxFrameLength(client.getMaxFrameLength());
|
||||
parser.setMaxSettingsKeys(client.getMaxSettingsKeys());
|
||||
|
||||
RetainableByteBufferPool retainableByteBufferPool = byteBufferPool.asRetainableByteBufferPool();
|
||||
|
||||
HTTP2ClientConnection connection = new HTTP2ClientConnection(client, retainableByteBufferPool, executor, endPoint,
|
||||
parser, session, client.getInputBufferSize(), promise, listener);
|
||||
session, client.getInputBufferSize(), sessionPromise, listener);
|
||||
connection.setUseInputDirectByteBuffers(client.isUseInputDirectByteBuffers());
|
||||
connection.setUseOutputDirectByteBuffers(client.isUseOutputDirectByteBuffers());
|
||||
connection.addEventListener(connectionListener);
|
||||
parser.init(connection);
|
||||
|
||||
return customize(connection, context);
|
||||
}
|
||||
|
||||
|
@ -84,11 +89,11 @@ public class HTTP2ClientConnectionFactory implements ClientConnectionFactory
|
|||
private final Promise<Session> promise;
|
||||
private final Session.Listener listener;
|
||||
|
||||
private HTTP2ClientConnection(HTTP2Client client, RetainableByteBufferPool retainableByteBufferPool, Executor executor, EndPoint endpoint, Parser parser, ISession session, int bufferSize, Promise<Session> promise, Session.Listener listener)
|
||||
private HTTP2ClientConnection(HTTP2Client client, RetainableByteBufferPool retainableByteBufferPool, Executor executor, EndPoint endpoint, HTTP2ClientSession session, int bufferSize, Promise<Session> sessionPromise, Session.Listener listener)
|
||||
{
|
||||
super(retainableByteBufferPool, executor, endpoint, parser, session, bufferSize);
|
||||
super(retainableByteBufferPool, executor, endpoint, session, bufferSize);
|
||||
this.client = client;
|
||||
this.promise = promise;
|
||||
this.promise = sessionPromise;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
|
@ -98,12 +103,52 @@ public class HTTP2ClientConnectionFactory implements ClientConnectionFactory
|
|||
Map<Integer, Integer> settings = listener.onPreface(getSession());
|
||||
if (settings == null)
|
||||
settings = new HashMap<>();
|
||||
settings.computeIfAbsent(SettingsFrame.INITIAL_WINDOW_SIZE, k -> client.getInitialStreamRecvWindow());
|
||||
settings.computeIfAbsent(SettingsFrame.MAX_CONCURRENT_STREAMS, k -> client.getMaxConcurrentPushedStreams());
|
||||
|
||||
Integer maxFrameLength = settings.get(SettingsFrame.MAX_FRAME_SIZE);
|
||||
if (maxFrameLength != null)
|
||||
getParser().setMaxFrameLength(maxFrameLength);
|
||||
// Below we want to populate any settings to send to the server
|
||||
// that have a different default than what prescribed by the RFC.
|
||||
// Changing the configuration is done when the SETTINGS is sent.
|
||||
|
||||
settings.compute(SettingsFrame.HEADER_TABLE_SIZE, (k, v) ->
|
||||
{
|
||||
if (v == null)
|
||||
{
|
||||
v = client.getMaxDecoderTableCapacity();
|
||||
if (v == HpackContext.DEFAULT_MAX_TABLE_CAPACITY)
|
||||
v = null;
|
||||
}
|
||||
return v;
|
||||
});
|
||||
settings.computeIfAbsent(SettingsFrame.MAX_CONCURRENT_STREAMS, k -> client.getMaxConcurrentPushedStreams());
|
||||
settings.compute(SettingsFrame.INITIAL_WINDOW_SIZE, (k, v) ->
|
||||
{
|
||||
if (v == null)
|
||||
{
|
||||
v = client.getInitialStreamRecvWindow();
|
||||
if (v == FlowControlStrategy.DEFAULT_WINDOW_SIZE)
|
||||
v = null;
|
||||
}
|
||||
return v;
|
||||
});
|
||||
settings.compute(SettingsFrame.MAX_FRAME_SIZE, (k, v) ->
|
||||
{
|
||||
if (v == null)
|
||||
{
|
||||
v = client.getMaxFrameSize();
|
||||
if (v == Frame.DEFAULT_MAX_LENGTH)
|
||||
v = null;
|
||||
}
|
||||
return v;
|
||||
});
|
||||
settings.compute(SettingsFrame.MAX_HEADER_LIST_SIZE, (k, v) ->
|
||||
{
|
||||
if (v == null)
|
||||
{
|
||||
v = client.getMaxResponseHeadersSize();
|
||||
if (v <= 0)
|
||||
v = null;
|
||||
}
|
||||
return v;
|
||||
});
|
||||
|
||||
PrefaceFrame prefaceFrame = new PrefaceFrame();
|
||||
SettingsFrame settingsFrame = new SettingsFrame(settings, false);
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
package org.eclipse.jetty.http2.client;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jetty.http.MetaData;
|
||||
import org.eclipse.jetty.http2.CloseState;
|
||||
import org.eclipse.jetty.http2.ErrorCode;
|
||||
|
@ -23,7 +25,9 @@ import org.eclipse.jetty.http2.api.Session;
|
|||
import org.eclipse.jetty.http2.api.Stream;
|
||||
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.generator.Generator;
|
||||
import org.eclipse.jetty.http2.parser.Parser;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
|
@ -34,9 +38,9 @@ public class HTTP2ClientSession extends HTTP2Session
|
|||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(HTTP2ClientSession.class);
|
||||
|
||||
public HTTP2ClientSession(Scheduler scheduler, EndPoint endPoint, Generator generator, Session.Listener listener, FlowControlStrategy flowControl)
|
||||
public HTTP2ClientSession(Scheduler scheduler, EndPoint endPoint, Parser parser, Generator generator, Session.Listener listener, FlowControlStrategy flowControl)
|
||||
{
|
||||
super(scheduler, endPoint, generator, listener, flowControl, 1);
|
||||
super(scheduler, endPoint, parser, generator, listener, flowControl, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -87,12 +91,30 @@ public class HTTP2ClientSession extends HTTP2Session
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSettings(SettingsFrame frame)
|
||||
{
|
||||
Map<Integer, Integer> settings = frame.getSettings();
|
||||
Integer value = settings.get(SettingsFrame.ENABLE_PUSH);
|
||||
// SPEC: servers can only send ENABLE_PUSH=0.
|
||||
if (value != null && value != 0)
|
||||
onConnectionFailure(ErrorCode.PROTOCOL_ERROR.code, "invalid_settings_frame");
|
||||
else
|
||||
super.onSettings(frame);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushPromise(PushPromiseFrame frame)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Received {}", frame);
|
||||
|
||||
if (!isPushEnabled())
|
||||
{
|
||||
onConnectionFailure(ErrorCode.PROTOCOL_ERROR.code, "unexpected_push_promise_frame");
|
||||
return;
|
||||
}
|
||||
|
||||
int streamId = frame.getStreamId();
|
||||
int pushStreamId = frame.getPromisedStreamId();
|
||||
IStream stream = getStream(streamId);
|
||||
|
|
|
@ -98,6 +98,7 @@ public class ConcurrentStreamCreationTest extends AbstractTest
|
|||
x.printStackTrace();
|
||||
}
|
||||
}).start());
|
||||
|
||||
assertTrue(clientLatch.await(total, TimeUnit.MILLISECONDS), String.format("Missing streams on client: %d/%d", clientLatch.getCount(), total));
|
||||
assertTrue(serverLatch.await(total, TimeUnit.MILLISECONDS), String.format("Missing streams on server: %d/%d", serverLatch.getCount(), total));
|
||||
assertTrue(responseLatch.await(total, TimeUnit.MILLISECONDS), String.format("Missing response on client: %d/%d", clientLatch.getCount(), total));
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.eclipse.jetty.http.HttpScheme;
|
|||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.http.MetaData;
|
||||
import org.eclipse.jetty.http2.AbstractFlowControlStrategy;
|
||||
import org.eclipse.jetty.http2.BufferingFlowControlStrategy;
|
||||
import org.eclipse.jetty.http2.ErrorCode;
|
||||
import org.eclipse.jetty.http2.FlowControlStrategy;
|
||||
|
@ -64,6 +65,7 @@ import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
|||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.awaitility.Awaitility.await;
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
|
@ -204,11 +206,9 @@ public abstract class FlowControlStrategyTest
|
|||
SettingsFrame frame = new SettingsFrame(settings, false);
|
||||
FutureCallback callback = new FutureCallback();
|
||||
clientSession.settings(frame, callback);
|
||||
callback.get(5, TimeUnit.SECONDS);
|
||||
|
||||
await().atMost(5, TimeUnit.SECONDS).until(() -> clientStream1.getRecvWindow() == 0);
|
||||
assertEquals(FlowControlStrategy.DEFAULT_WINDOW_SIZE, clientStream1.getSendWindow());
|
||||
assertEquals(0, clientStream1.getRecvWindow());
|
||||
settingsLatch.await(5, TimeUnit.SECONDS);
|
||||
|
||||
// Now create a new stream, it must pick up the new value.
|
||||
MetaData.Request request2 = newRequest("POST", HttpFields.EMPTY);
|
||||
|
@ -343,6 +343,11 @@ public abstract class FlowControlStrategyTest
|
|||
completable.thenRun(settingsLatch::countDown);
|
||||
|
||||
assertTrue(settingsLatch.await(5, TimeUnit.SECONDS));
|
||||
await().atMost(5, TimeUnit.SECONDS).until(() ->
|
||||
{
|
||||
AbstractFlowControlStrategy flow = (AbstractFlowControlStrategy)((HTTP2Session)session).getFlowControlStrategy();
|
||||
return flow.getInitialStreamRecvWindow() == windowSize;
|
||||
});
|
||||
|
||||
CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
Exchanger<Callback> exchanger = new Exchanger<>();
|
||||
|
@ -403,13 +408,14 @@ public abstract class FlowControlStrategyTest
|
|||
{
|
||||
int windowSize = 1536;
|
||||
Exchanger<Callback> exchanger = new Exchanger<>();
|
||||
CountDownLatch settingsLatch = new CountDownLatch(1);
|
||||
CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
AtomicReference<HTTP2Session> serverSessionRef = new AtomicReference<>();
|
||||
start(new ServerSessionListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public Map<Integer, Integer> onPreface(Session session)
|
||||
{
|
||||
serverSessionRef.set((HTTP2Session)session);
|
||||
Map<Integer, Integer> settings = new HashMap<>();
|
||||
settings.put(SettingsFrame.INITIAL_WINDOW_SIZE, windowSize);
|
||||
return settings;
|
||||
|
@ -458,21 +464,18 @@ public abstract class FlowControlStrategyTest
|
|||
}
|
||||
});
|
||||
|
||||
Session session = newClient(new Session.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onSettings(Session session, SettingsFrame frame)
|
||||
{
|
||||
settingsLatch.countDown();
|
||||
}
|
||||
});
|
||||
Session clientSession = newClient(new Session.Listener.Adapter());
|
||||
|
||||
assertTrue(settingsLatch.await(5, TimeUnit.SECONDS));
|
||||
await().atMost(5, TimeUnit.SECONDS).until(() ->
|
||||
{
|
||||
AbstractFlowControlStrategy flow = (AbstractFlowControlStrategy)serverSessionRef.get().getFlowControlStrategy();
|
||||
return flow.getInitialStreamRecvWindow() == windowSize;
|
||||
});
|
||||
|
||||
MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY);
|
||||
HeadersFrame requestFrame = new HeadersFrame(metaData, null, false);
|
||||
FuturePromise<Stream> streamPromise = new FuturePromise<>();
|
||||
session.newStream(requestFrame, streamPromise, null);
|
||||
clientSession.newStream(requestFrame, streamPromise, null);
|
||||
Stream stream = streamPromise.get(5, TimeUnit.SECONDS);
|
||||
|
||||
int length = 5 * windowSize;
|
||||
|
|
|
@ -36,6 +36,7 @@ import org.eclipse.jetty.http2.api.Session;
|
|||
import org.eclipse.jetty.http2.api.Stream;
|
||||
import org.eclipse.jetty.http2.api.server.ServerSessionListener;
|
||||
import org.eclipse.jetty.http2.frames.DataFrame;
|
||||
import org.eclipse.jetty.http2.frames.FrameType;
|
||||
import org.eclipse.jetty.http2.frames.GoAwayFrame;
|
||||
import org.eclipse.jetty.http2.frames.HeadersFrame;
|
||||
import org.eclipse.jetty.http2.frames.ResetFrame;
|
||||
|
@ -1103,4 +1104,70 @@ public class GoAwayTest extends AbstractTest
|
|||
Assertions.assertFalse(((HTTP2Session)serverSessionRef.get()).getEndPoint().isOpen());
|
||||
Assertions.assertFalse(((HTTP2Session)clientSession).getEndPoint().isOpen());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGoAwayNonZeroStreamId() throws Exception
|
||||
{
|
||||
CountDownLatch serverGoAwayLatch = new CountDownLatch(1);
|
||||
CountDownLatch serverFailureLatch = new CountDownLatch(1);
|
||||
CountDownLatch serverCloseLatch = new CountDownLatch(1);
|
||||
start(new ServerSessionListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onGoAway(Session session, GoAwayFrame frame)
|
||||
{
|
||||
serverGoAwayLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Session session, Throwable failure)
|
||||
{
|
||||
serverFailureLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(Session session, GoAwayFrame frame)
|
||||
{
|
||||
serverCloseLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
CountDownLatch clientGoAwayLatch = new CountDownLatch(1);
|
||||
CountDownLatch clientCloseLatch = new CountDownLatch(1);
|
||||
Session clientSession = newClient(new Session.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onGoAway(Session session, GoAwayFrame frame)
|
||||
{
|
||||
clientGoAwayLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(Session session, GoAwayFrame frame)
|
||||
{
|
||||
clientCloseLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
// Wait until the client has finished the previous writes.
|
||||
Thread.sleep(1000);
|
||||
// Write an invalid GOAWAY frame.
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocate(17)
|
||||
.put((byte)0)
|
||||
.put((byte)0)
|
||||
.put((byte)8)
|
||||
.put((byte)FrameType.GO_AWAY.getType())
|
||||
.put((byte)0)
|
||||
.putInt(1) // Non-Zero Stream ID
|
||||
.putInt(0)
|
||||
.putInt(ErrorCode.PROTOCOL_ERROR.code)
|
||||
.flip();
|
||||
((HTTP2Session)clientSession).getEndPoint().write(Callback.NOOP, byteBuffer);
|
||||
|
||||
Assertions.assertFalse(serverGoAwayLatch.await(1, TimeUnit.SECONDS));
|
||||
Assertions.assertTrue(serverFailureLatch.await(5, TimeUnit.SECONDS));
|
||||
Assertions.assertTrue(serverCloseLatch.await(5, TimeUnit.SECONDS));
|
||||
Assertions.assertTrue(clientGoAwayLatch.await(5, TimeUnit.SECONDS));
|
||||
Assertions.assertTrue(clientCloseLatch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,10 +47,7 @@ import org.eclipse.jetty.http2.frames.HeadersFrame;
|
|||
import org.eclipse.jetty.http2.frames.ResetFrame;
|
||||
import org.eclipse.jetty.http2.frames.SettingsFrame;
|
||||
import org.eclipse.jetty.http2.hpack.HpackException;
|
||||
import org.eclipse.jetty.http2.parser.RateControl;
|
||||
import org.eclipse.jetty.http2.parser.ServerParser;
|
||||
import org.eclipse.jetty.http2.server.RawHTTP2ServerConnectionFactory;
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
|
@ -737,6 +734,7 @@ public class HTTP2Test extends AbstractTest
|
|||
@Test
|
||||
public void testGoAwayRespondedWithGoAway() throws Exception
|
||||
{
|
||||
CountDownLatch goAwayLatch = new CountDownLatch(1);
|
||||
ServerSessionListener.Adapter serverListener = new ServerSessionListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
|
@ -748,24 +746,14 @@ public class HTTP2Test extends AbstractTest
|
|||
stream.getSession().close(ErrorCode.NO_ERROR.code, null, Callback.NOOP);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
CountDownLatch goAwayLatch = new CountDownLatch(1);
|
||||
RawHTTP2ServerConnectionFactory connectionFactory = new RawHTTP2ServerConnectionFactory(new HttpConfiguration(), serverListener)
|
||||
{
|
||||
|
||||
@Override
|
||||
protected ServerParser newServerParser(Connector connector, ServerParser.Listener listener, RateControl rateControl)
|
||||
public void onGoAway(Session session, GoAwayFrame frame)
|
||||
{
|
||||
return super.newServerParser(connector, new ServerParser.Listener.Wrapper(listener)
|
||||
{
|
||||
@Override
|
||||
public void onGoAway(GoAwayFrame frame)
|
||||
{
|
||||
super.onGoAway(frame);
|
||||
goAwayLatch.countDown();
|
||||
}
|
||||
}, rateControl);
|
||||
goAwayLatch.countDown();
|
||||
}
|
||||
};
|
||||
RawHTTP2ServerConnectionFactory connectionFactory = new RawHTTP2ServerConnectionFactory(new HttpConfiguration(), serverListener);
|
||||
prepareServer(connectionFactory);
|
||||
server.start();
|
||||
|
||||
|
|
|
@ -30,7 +30,6 @@ import java.util.concurrent.CountDownLatch;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
|
@ -145,6 +144,7 @@ public class PrefaceTest extends AbstractTest
|
|||
session.close(ErrorCode.NO_ERROR.code, null, Callback.NOOP);
|
||||
}
|
||||
});
|
||||
connector.setIdleTimeout(1000);
|
||||
|
||||
ByteBufferPool byteBufferPool = client.getByteBufferPool();
|
||||
try (SocketChannel socket = SocketChannel.open())
|
||||
|
@ -164,15 +164,15 @@ public class PrefaceTest extends AbstractTest
|
|||
socket.write(buffers.toArray(new ByteBuffer[buffers.size()]));
|
||||
|
||||
Queue<SettingsFrame> settings = new ArrayDeque<>();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onSettings(SettingsFrame frame)
|
||||
{
|
||||
settings.offer(frame);
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
ByteBuffer buffer = byteBufferPool.acquire(1024, true);
|
||||
while (true)
|
||||
|
@ -297,7 +297,8 @@ public class PrefaceTest extends AbstractTest
|
|||
|
||||
CountDownLatch clientSettingsLatch = new CountDownLatch(1);
|
||||
AtomicBoolean responded = new AtomicBoolean();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onSettings(SettingsFrame frame)
|
||||
|
@ -314,8 +315,7 @@ public class PrefaceTest extends AbstractTest
|
|||
if (frame.isEndStream())
|
||||
responded.set(true);
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
// HTTP/2 parsing.
|
||||
while (true)
|
||||
|
|
|
@ -0,0 +1,357 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
|
||||
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.http2.client;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.http.MetaData;
|
||||
import org.eclipse.jetty.http2.Flags;
|
||||
import org.eclipse.jetty.http2.HTTP2Session;
|
||||
import org.eclipse.jetty.http2.api.Session;
|
||||
import org.eclipse.jetty.http2.api.Stream;
|
||||
import org.eclipse.jetty.http2.api.server.ServerSessionListener;
|
||||
import org.eclipse.jetty.http2.frames.FrameType;
|
||||
import org.eclipse.jetty.http2.frames.GoAwayFrame;
|
||||
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.hpack.HpackException;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class SettingsTest extends AbstractTest
|
||||
{
|
||||
@Test
|
||||
public void testSettingsNonZeroStreamId() throws Exception
|
||||
{
|
||||
AtomicReference<CountDownLatch> serverSettingsLatch = new AtomicReference<>(null);
|
||||
CountDownLatch serverFailureLatch = new CountDownLatch(1);
|
||||
CountDownLatch serverCloseLatch = new CountDownLatch(1);
|
||||
start(new ServerSessionListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onSettings(Session session, SettingsFrame frame)
|
||||
{
|
||||
CountDownLatch latch = serverSettingsLatch.get();
|
||||
if (latch != null)
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Session session, Throwable failure)
|
||||
{
|
||||
serverFailureLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(Session session, GoAwayFrame frame)
|
||||
{
|
||||
serverCloseLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
CountDownLatch clientGoAwayLatch = new CountDownLatch(1);
|
||||
CountDownLatch clientCloseLatch = new CountDownLatch(1);
|
||||
Session clientSession = newClient(new Session.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onGoAway(Session session, GoAwayFrame frame)
|
||||
{
|
||||
clientGoAwayLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(Session session, GoAwayFrame frame)
|
||||
{
|
||||
clientCloseLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
// Wait until the client has finished the previous writes.
|
||||
Thread.sleep(1000);
|
||||
// Set the SETTINGS latch now, to avoid that it
|
||||
// is counted down during connection establishment.
|
||||
serverSettingsLatch.set(new CountDownLatch(1));
|
||||
// Write an invalid SETTINGS frame.
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocate(17)
|
||||
.put((byte)0)
|
||||
.put((byte)0)
|
||||
.put((byte)0)
|
||||
.put((byte)FrameType.SETTINGS.getType())
|
||||
.put((byte)0)
|
||||
.putInt(1) // Non-Zero Stream ID
|
||||
.flip();
|
||||
((HTTP2Session)clientSession).getEndPoint().write(Callback.NOOP, byteBuffer);
|
||||
|
||||
Assertions.assertFalse(serverSettingsLatch.get().await(1, TimeUnit.SECONDS));
|
||||
Assertions.assertTrue(serverFailureLatch.await(5, TimeUnit.SECONDS));
|
||||
Assertions.assertTrue(serverCloseLatch.await(5, TimeUnit.SECONDS));
|
||||
Assertions.assertTrue(clientGoAwayLatch.await(5, TimeUnit.SECONDS));
|
||||
Assertions.assertTrue(clientCloseLatch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSettingsReplyWithPayload() throws Exception
|
||||
{
|
||||
AtomicReference<CountDownLatch> serverSettingsLatch = new AtomicReference<>(null);
|
||||
CountDownLatch serverFailureLatch = new CountDownLatch(1);
|
||||
CountDownLatch serverCloseLatch = new CountDownLatch(1);
|
||||
start(new ServerSessionListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onSettings(Session session, SettingsFrame frame)
|
||||
{
|
||||
CountDownLatch latch = serverSettingsLatch.get();
|
||||
if (latch != null)
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Session session, Throwable failure)
|
||||
{
|
||||
serverFailureLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(Session session, GoAwayFrame frame)
|
||||
{
|
||||
serverCloseLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
CountDownLatch clientGoAwayLatch = new CountDownLatch(1);
|
||||
CountDownLatch clientCloseLatch = new CountDownLatch(1);
|
||||
Session clientSession = newClient(new Session.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onGoAway(Session session, GoAwayFrame frame)
|
||||
{
|
||||
clientGoAwayLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(Session session, GoAwayFrame frame)
|
||||
{
|
||||
clientCloseLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
// Wait until the client has finished the previous writes.
|
||||
Thread.sleep(1000);
|
||||
// Set the SETTINGS latch now, to avoid that it
|
||||
// is counted down during connection establishment.
|
||||
serverSettingsLatch.set(new CountDownLatch(1));
|
||||
// Write an invalid SETTINGS frame.
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocate(17)
|
||||
.put((byte)0)
|
||||
.put((byte)0)
|
||||
.put((byte)6)
|
||||
.put((byte)FrameType.SETTINGS.getType())
|
||||
.put((byte)Flags.ACK)
|
||||
.putInt(0)
|
||||
.putShort((short)SettingsFrame.ENABLE_PUSH)
|
||||
.putInt(1)
|
||||
.flip();
|
||||
((HTTP2Session)clientSession).getEndPoint().write(Callback.NOOP, byteBuffer);
|
||||
|
||||
Assertions.assertFalse(serverSettingsLatch.get().await(1, TimeUnit.SECONDS));
|
||||
Assertions.assertTrue(serverFailureLatch.await(5, TimeUnit.SECONDS));
|
||||
Assertions.assertTrue(serverCloseLatch.await(5, TimeUnit.SECONDS));
|
||||
Assertions.assertTrue(clientGoAwayLatch.await(5, TimeUnit.SECONDS));
|
||||
Assertions.assertTrue(clientCloseLatch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidEnablePush() throws Exception
|
||||
{
|
||||
CountDownLatch serverFailureLatch = new CountDownLatch(1);
|
||||
start(new ServerSessionListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onFailure(Session session, Throwable failure)
|
||||
{
|
||||
serverFailureLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
newClient(new Session.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public Map<Integer, Integer> onPreface(Session session)
|
||||
{
|
||||
Map<Integer, Integer> settings = new HashMap<>();
|
||||
settings.put(SettingsFrame.ENABLE_PUSH, 2); // Invalid value.
|
||||
return settings;
|
||||
}
|
||||
});
|
||||
|
||||
Assertions.assertTrue(serverFailureLatch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServerSendsEnablePush() throws Exception
|
||||
{
|
||||
start(new ServerSessionListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public Map<Integer, Integer> onPreface(Session session)
|
||||
{
|
||||
Map<Integer, Integer> settings = new HashMap<>();
|
||||
// Servers cannot send "enable_push==1".
|
||||
settings.put(SettingsFrame.ENABLE_PUSH, 1);
|
||||
return settings;
|
||||
}
|
||||
});
|
||||
|
||||
CountDownLatch clientFailureLatch = new CountDownLatch(1);
|
||||
newClient(new Session.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onFailure(Session session, Throwable failure)
|
||||
{
|
||||
clientFailureLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
Assertions.assertTrue(clientFailureLatch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServerCannotSendsPushPromiseWithPushDisabled() throws Exception
|
||||
{
|
||||
CountDownLatch serverPushFailureLatch = new CountDownLatch(1);
|
||||
start(new ServerSessionListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
|
||||
{
|
||||
MetaData.Response response = new MetaData.Response(HttpVersion.HTTP_2, HttpStatus.OK_200, HttpFields.EMPTY);
|
||||
stream.headers(new HeadersFrame(stream.getId(), response, null, true))
|
||||
.thenAccept(s ->
|
||||
{
|
||||
MetaData.Request push = newRequest("GET", "/push", HttpFields.EMPTY);
|
||||
try
|
||||
{
|
||||
s.push(new PushPromiseFrame(s.getId(), push), new Stream.Listener.Adapter());
|
||||
}
|
||||
catch (IllegalStateException x)
|
||||
{
|
||||
serverPushFailureLatch.countDown();
|
||||
}
|
||||
});
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
Session clientSession = newClient(new Session.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public Map<Integer, Integer> onPreface(Session session)
|
||||
{
|
||||
Map<Integer, Integer> settings = new HashMap<>();
|
||||
// Disable push.
|
||||
settings.put(SettingsFrame.ENABLE_PUSH, 0);
|
||||
return settings;
|
||||
}
|
||||
});
|
||||
|
||||
CountDownLatch clientResponseLatch = new CountDownLatch(1);
|
||||
CountDownLatch clientPushLatch = new CountDownLatch(1);
|
||||
MetaData.Request request = newRequest("GET", HttpFields.EMPTY);
|
||||
HeadersFrame frame = new HeadersFrame(request, null, true);
|
||||
clientSession.newStream(frame, new Stream.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onHeaders(Stream stream, HeadersFrame frame)
|
||||
{
|
||||
clientResponseLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream.Listener onPush(Stream stream, PushPromiseFrame frame)
|
||||
{
|
||||
clientPushLatch.countDown();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
Assertions.assertTrue(serverPushFailureLatch.await(5, TimeUnit.SECONDS));
|
||||
Assertions.assertTrue(clientResponseLatch.await(5, TimeUnit.SECONDS));
|
||||
Assertions.assertFalse(clientPushLatch.await(1, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClientReceivesPushPromiseWhenPushDisabled() throws Exception
|
||||
{
|
||||
start(new ServerSessionListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
|
||||
{
|
||||
try
|
||||
{
|
||||
HTTP2Session session = (HTTP2Session)stream.getSession();
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(connector.getByteBufferPool());
|
||||
MetaData.Request push = newRequest("GET", "/push", HttpFields.EMPTY);
|
||||
PushPromiseFrame pushFrame = new PushPromiseFrame(stream.getId(), 2, push);
|
||||
session.getGenerator().control(lease, pushFrame);
|
||||
session.getEndPoint().write(Callback.NOOP, lease.getByteBuffers().toArray(ByteBuffer[]::new));
|
||||
return null;
|
||||
}
|
||||
catch (HpackException x)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
CountDownLatch clientFailureLatch = new CountDownLatch(1);
|
||||
Session clientSession = newClient(new Session.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public Map<Integer, Integer> onPreface(Session session)
|
||||
{
|
||||
Map<Integer, Integer> settings = new HashMap<>();
|
||||
// Disable push.
|
||||
settings.put(SettingsFrame.ENABLE_PUSH, 0);
|
||||
return settings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Session session, Throwable failure)
|
||||
{
|
||||
clientFailureLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
// Wait until the server has finished the previous writes.
|
||||
Thread.sleep(1000);
|
||||
|
||||
MetaData.Request request = newRequest("GET", HttpFields.EMPTY);
|
||||
HeadersFrame frame = new HeadersFrame(request, null, true);
|
||||
clientSession.newStream(frame, new Stream.Listener.Adapter());
|
||||
|
||||
Assertions.assertTrue(clientFailureLatch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
|
@ -40,6 +40,7 @@ import org.eclipse.jetty.http2.frames.DataFrame;
|
|||
import org.eclipse.jetty.http2.frames.Frame;
|
||||
import org.eclipse.jetty.http2.frames.HeadersFrame;
|
||||
import org.eclipse.jetty.http2.frames.ResetFrame;
|
||||
import org.eclipse.jetty.http2.generator.Generator;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
|
@ -368,7 +369,8 @@ public class TrailersTest extends AbstractTest
|
|||
completable.thenRun(() ->
|
||||
{
|
||||
// Disable checks for invalid headers.
|
||||
((HTTP2Session)session).getGenerator().setValidateHpackEncoding(false);
|
||||
Generator generator = ((HTTP2Session)session).getGenerator();
|
||||
generator.getHpackEncoder().setValidateEncoding(false);
|
||||
// Invalid trailer: cannot contain pseudo headers.
|
||||
HttpFields.Mutable trailerFields = HttpFields.build();
|
||||
trailerFields.put(HttpHeader.C_METHOD, "GET");
|
||||
|
|
|
@ -21,6 +21,14 @@ import java.util.concurrent.Executor;
|
|||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.eclipse.jetty.http2.frames.DataFrame;
|
||||
import org.eclipse.jetty.http2.frames.GoAwayFrame;
|
||||
import org.eclipse.jetty.http2.frames.HeadersFrame;
|
||||
import org.eclipse.jetty.http2.frames.PingFrame;
|
||||
import org.eclipse.jetty.http2.frames.PriorityFrame;
|
||||
import org.eclipse.jetty.http2.frames.PushPromiseFrame;
|
||||
import org.eclipse.jetty.http2.frames.ResetFrame;
|
||||
import org.eclipse.jetty.http2.frames.SettingsFrame;
|
||||
import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
|
||||
import org.eclipse.jetty.http2.parser.Parser;
|
||||
import org.eclipse.jetty.io.AbstractConnection;
|
||||
import org.eclipse.jetty.io.Connection;
|
||||
|
@ -37,7 +45,7 @@ import org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class HTTP2Connection extends AbstractConnection implements WriteFlusher.Listener, Connection.UpgradeTo
|
||||
public class HTTP2Connection extends AbstractConnection implements Parser.Listener, WriteFlusher.Listener, Connection.UpgradeTo
|
||||
{
|
||||
protected static final Logger LOG = LoggerFactory.getLogger(HTTP2Connection.class);
|
||||
|
||||
|
@ -46,23 +54,20 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher.
|
|||
private final HTTP2Producer producer = new HTTP2Producer();
|
||||
private final AtomicLong bytesIn = new AtomicLong();
|
||||
private final RetainableByteBufferPool retainableByteBufferPool;
|
||||
private final Parser parser;
|
||||
private final ISession session;
|
||||
private final HTTP2Session session;
|
||||
private final int bufferSize;
|
||||
private final ExecutionStrategy strategy;
|
||||
private boolean useInputDirectByteBuffers;
|
||||
private boolean useOutputDirectByteBuffers;
|
||||
|
||||
protected HTTP2Connection(RetainableByteBufferPool retainableByteBufferPool, Executor executor, EndPoint endPoint, Parser parser, ISession session, int bufferSize)
|
||||
protected HTTP2Connection(RetainableByteBufferPool retainableByteBufferPool, Executor executor, EndPoint endPoint, HTTP2Session session, int bufferSize)
|
||||
{
|
||||
super(endPoint, executor);
|
||||
this.retainableByteBufferPool = retainableByteBufferPool;
|
||||
this.parser = parser;
|
||||
this.session = session;
|
||||
this.bufferSize = bufferSize;
|
||||
this.strategy = new AdaptiveExecutionStrategy(producer, executor);
|
||||
LifeCycle.start(strategy);
|
||||
parser.init(ParserListener::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -96,11 +101,6 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher.
|
|||
return session;
|
||||
}
|
||||
|
||||
protected Parser getParser()
|
||||
{
|
||||
return parser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgradeTo(ByteBuffer buffer)
|
||||
{
|
||||
|
@ -231,6 +231,78 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher.
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHeaders(HeadersFrame frame)
|
||||
{
|
||||
session.onHeaders(frame);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onData(DataFrame frame)
|
||||
{
|
||||
NetworkBuffer networkBuffer = producer.networkBuffer;
|
||||
// Retain the network buffer because the frame payload is a slice of it.
|
||||
networkBuffer.retain();
|
||||
// The network buffer is also the callback used to release the frame payload.
|
||||
Callback callback = networkBuffer;
|
||||
session.onData(frame, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPriority(PriorityFrame frame)
|
||||
{
|
||||
session.onPriority(frame);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReset(ResetFrame frame)
|
||||
{
|
||||
session.onReset(frame);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSettings(SettingsFrame frame)
|
||||
{
|
||||
session.onSettings(frame);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushPromise(PushPromiseFrame frame)
|
||||
{
|
||||
session.onPushPromise(frame);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPing(PingFrame frame)
|
||||
{
|
||||
session.onPing(frame);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGoAway(GoAwayFrame frame)
|
||||
{
|
||||
session.onGoAway(frame);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowUpdate(WindowUpdateFrame frame)
|
||||
{
|
||||
session.onWindowUpdate(frame);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStreamFailure(int streamId, int error, String reason)
|
||||
{
|
||||
session.onStreamFailure(streamId, error, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionFailure(int error, String reason)
|
||||
{
|
||||
producer.failed = true;
|
||||
session.onConnectionFailure(error, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFlushed(long bytes) throws IOException
|
||||
{
|
||||
|
@ -275,7 +347,7 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher.
|
|||
{
|
||||
while (networkBuffer.hasRemaining())
|
||||
{
|
||||
parser.parse(networkBuffer.getBuffer());
|
||||
session.getParser().parse(networkBuffer.getBuffer());
|
||||
if (failed)
|
||||
return null;
|
||||
}
|
||||
|
@ -391,30 +463,6 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher.
|
|||
}
|
||||
}
|
||||
|
||||
private class ParserListener extends Parser.Listener.Wrapper
|
||||
{
|
||||
private ParserListener(Parser.Listener listener)
|
||||
{
|
||||
super(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onData(DataFrame frame)
|
||||
{
|
||||
NetworkBuffer networkBuffer = producer.networkBuffer;
|
||||
networkBuffer.retain();
|
||||
Callback callback = networkBuffer;
|
||||
session.onData(frame, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionFailure(int error, String reason)
|
||||
{
|
||||
producer.failed = true;
|
||||
super.onConnectionFailure(error, reason);
|
||||
}
|
||||
}
|
||||
|
||||
private class NetworkBuffer implements Callback
|
||||
{
|
||||
private final RetainableByteBuffer delegate;
|
||||
|
|
|
@ -54,6 +54,7 @@ import org.eclipse.jetty.http2.frames.SettingsFrame;
|
|||
import org.eclipse.jetty.http2.frames.StreamFrame;
|
||||
import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
|
||||
import org.eclipse.jetty.http2.generator.Generator;
|
||||
import org.eclipse.jetty.http2.hpack.HpackEncoder;
|
||||
import org.eclipse.jetty.http2.hpack.HpackException;
|
||||
import org.eclipse.jetty.http2.parser.Parser;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
|
@ -94,6 +95,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
private final AtomicInteger recvWindow = new AtomicInteger();
|
||||
private final AtomicLong bytesWritten = new AtomicLong();
|
||||
private final EndPoint endPoint;
|
||||
private final Parser parser;
|
||||
private final Generator generator;
|
||||
private final Session.Listener listener;
|
||||
private final FlowControlStrategy flowControl;
|
||||
|
@ -104,12 +106,14 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
private long streamIdleTimeout;
|
||||
private int initialSessionRecvWindow;
|
||||
private int writeThreshold;
|
||||
private int maxEncoderTableCapacity;
|
||||
private boolean pushEnabled;
|
||||
private boolean connectProtocolEnabled;
|
||||
|
||||
public HTTP2Session(Scheduler scheduler, EndPoint endPoint, Generator generator, Session.Listener listener, FlowControlStrategy flowControl, int initialStreamId)
|
||||
public HTTP2Session(Scheduler scheduler, EndPoint endPoint, Parser parser, Generator generator, Session.Listener listener, FlowControlStrategy flowControl, int initialStreamId)
|
||||
{
|
||||
this.endPoint = endPoint;
|
||||
this.parser = parser;
|
||||
this.generator = generator;
|
||||
this.listener = listener;
|
||||
this.flowControl = flowControl;
|
||||
|
@ -207,11 +211,27 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
this.writeThreshold = writeThreshold;
|
||||
}
|
||||
|
||||
@ManagedAttribute("The HPACK encoder dynamic table maximum capacity")
|
||||
public int getMaxEncoderTableCapacity()
|
||||
{
|
||||
return maxEncoderTableCapacity;
|
||||
}
|
||||
|
||||
public void setMaxEncoderTableCapacity(int maxEncoderTableCapacity)
|
||||
{
|
||||
this.maxEncoderTableCapacity = maxEncoderTableCapacity;
|
||||
}
|
||||
|
||||
public EndPoint getEndPoint()
|
||||
{
|
||||
return endPoint;
|
||||
}
|
||||
|
||||
public Parser getParser()
|
||||
{
|
||||
return parser;
|
||||
}
|
||||
|
||||
public Generator getGenerator()
|
||||
{
|
||||
return generator;
|
||||
|
@ -348,8 +368,20 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
if (frame.isReply())
|
||||
return;
|
||||
|
||||
// Iterate over all settings
|
||||
for (Map.Entry<Integer, Integer> entry : frame.getSettings().entrySet())
|
||||
Map<Integer, Integer> settings = frame.getSettings();
|
||||
configure(settings, false);
|
||||
notifySettings(this, frame);
|
||||
|
||||
if (reply)
|
||||
{
|
||||
SettingsFrame replyFrame = new SettingsFrame(Collections.emptyMap(), true);
|
||||
settings(replyFrame, Callback.NOOP);
|
||||
}
|
||||
}
|
||||
|
||||
private void configure(Map<Integer, Integer> settings, boolean local)
|
||||
{
|
||||
for (Map.Entry<Integer, Integer> entry : settings.entrySet())
|
||||
{
|
||||
int key = entry.getKey();
|
||||
int value = entry.getValue();
|
||||
|
@ -358,8 +390,17 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
case SettingsFrame.HEADER_TABLE_SIZE:
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Updating HPACK header table size to {} for {}", value, this);
|
||||
generator.setHeaderTableSize(value);
|
||||
LOG.debug("Updating HPACK {} max table capacity to {} for {}", local ? "decoder" : "encoder", value, this);
|
||||
if (local)
|
||||
{
|
||||
parser.getHpackDecoder().setMaxTableCapacity(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
HpackEncoder hpackEncoder = generator.getHpackEncoder();
|
||||
hpackEncoder.setMaxTableCapacity(value);
|
||||
hpackEncoder.setTableCapacity(Math.min(value, getMaxEncoderTableCapacity()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SettingsFrame.ENABLE_PUSH:
|
||||
|
@ -373,29 +414,38 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
case SettingsFrame.MAX_CONCURRENT_STREAMS:
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Updating max local concurrent streams to {} for {}", value, this);
|
||||
maxLocalStreams = value;
|
||||
LOG.debug("Updating max {} concurrent streams to {} for {}", local ? "remote" : "local", value, this);
|
||||
if (local)
|
||||
maxRemoteStreams = value;
|
||||
else
|
||||
maxLocalStreams = value;
|
||||
break;
|
||||
}
|
||||
case SettingsFrame.INITIAL_WINDOW_SIZE:
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Updating initial stream window size to {} for {}", value, this);
|
||||
flowControl.updateInitialStreamWindow(this, value, false);
|
||||
flowControl.updateInitialStreamWindow(this, value, local);
|
||||
break;
|
||||
}
|
||||
case SettingsFrame.MAX_FRAME_SIZE:
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Updating max frame size to {} for {}", value, this);
|
||||
generator.setMaxFrameSize(value);
|
||||
LOG.debug("Updating {} max frame size to {} for {}", local ? "parser" : "generator", value, this);
|
||||
if (local)
|
||||
parser.setMaxFrameSize(value);
|
||||
else
|
||||
generator.setMaxFrameSize(value);
|
||||
break;
|
||||
}
|
||||
case SettingsFrame.MAX_HEADER_LIST_SIZE:
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Updating max header list size to {} for {}", value, this);
|
||||
generator.setMaxHeaderListSize(value);
|
||||
LOG.debug("Updating {} max header list size to {} for {}", local ? "decoder" : "encoder", value, this);
|
||||
if (local)
|
||||
parser.getHpackDecoder().setMaxHeaderListSize(value);
|
||||
else
|
||||
generator.getHpackEncoder().setMaxHeaderListSize(value);
|
||||
break;
|
||||
}
|
||||
case SettingsFrame.ENABLE_CONNECT_PROTOCOL:
|
||||
|
@ -414,13 +464,6 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
}
|
||||
}
|
||||
}
|
||||
notifySettings(this, frame);
|
||||
|
||||
if (reply)
|
||||
{
|
||||
SettingsFrame replyFrame = new SettingsFrame(Collections.emptyMap(), true);
|
||||
settings(replyFrame, Callback.NOOP);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -629,6 +672,8 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
@Override
|
||||
public void push(IStream stream, Promise<Stream> promise, PushPromiseFrame frame, Stream.Listener listener)
|
||||
{
|
||||
if (!isPushEnabled())
|
||||
throw new IllegalStateException("Push is disabled");
|
||||
streamsState.push(frame, new Promise.Wrapper<>(promise)
|
||||
{
|
||||
@Override
|
||||
|
@ -1283,9 +1328,8 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
case SETTINGS:
|
||||
{
|
||||
SettingsFrame settingsFrame = (SettingsFrame)frame;
|
||||
Integer initialWindow = settingsFrame.getSettings().get(SettingsFrame.INITIAL_WINDOW_SIZE);
|
||||
if (initialWindow != null)
|
||||
flowControl.updateInitialStreamWindow(HTTP2Session.this, initialWindow, true);
|
||||
if (!settingsFrame.isReply())
|
||||
configure(settingsFrame.getSettings(), true);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
|
|
@ -30,20 +30,32 @@ public class Generator
|
|||
|
||||
public Generator(ByteBufferPool byteBufferPool)
|
||||
{
|
||||
this(byteBufferPool, 4096, 0);
|
||||
this(byteBufferPool, 0);
|
||||
}
|
||||
|
||||
public Generator(ByteBufferPool byteBufferPool, int maxDynamicTableSize, int maxHeaderBlockFragment)
|
||||
@Deprecated
|
||||
public Generator(ByteBufferPool byteBufferPool, int maxTableCapacity, int maxHeaderBlockFragment)
|
||||
{
|
||||
this(byteBufferPool, true, maxDynamicTableSize, maxHeaderBlockFragment);
|
||||
this(byteBufferPool, maxHeaderBlockFragment);
|
||||
}
|
||||
|
||||
public Generator(ByteBufferPool byteBufferPool, boolean useDirectByteBuffers, int maxDynamicTableSize, int maxHeaderBlockFragment)
|
||||
@Deprecated
|
||||
public Generator(ByteBufferPool byteBufferPool, boolean useDirectByteBuffers, int maxTableCapacity, int maxHeaderBlockFragment)
|
||||
{
|
||||
this(byteBufferPool, useDirectByteBuffers, maxHeaderBlockFragment);
|
||||
}
|
||||
|
||||
public Generator(ByteBufferPool byteBufferPool, int maxHeaderBlockFragment)
|
||||
{
|
||||
this(byteBufferPool, true, maxHeaderBlockFragment);
|
||||
}
|
||||
|
||||
public Generator(ByteBufferPool byteBufferPool, boolean useDirectByteBuffers, int maxHeaderBlockFragment)
|
||||
{
|
||||
this.byteBufferPool = byteBufferPool;
|
||||
|
||||
headerGenerator = new HeaderGenerator(useDirectByteBuffers);
|
||||
hpackEncoder = new HpackEncoder(maxDynamicTableSize);
|
||||
hpackEncoder = new HpackEncoder();
|
||||
|
||||
this.generators = new FrameGenerator[FrameType.values().length];
|
||||
this.generators[FrameType.HEADERS.getType()] = new HeadersGenerator(headerGenerator, hpackEncoder, maxHeaderBlockFragment);
|
||||
|
@ -66,14 +78,21 @@ public class Generator
|
|||
return byteBufferPool;
|
||||
}
|
||||
|
||||
public void setValidateHpackEncoding(boolean validateEncoding)
|
||||
public HpackEncoder getHpackEncoder()
|
||||
{
|
||||
hpackEncoder.setValidateEncoding(validateEncoding);
|
||||
return hpackEncoder;
|
||||
}
|
||||
|
||||
public void setHeaderTableSize(int headerTableSize)
|
||||
@Deprecated
|
||||
public void setValidateHpackEncoding(boolean validateEncoding)
|
||||
{
|
||||
hpackEncoder.setRemoteMaxDynamicTableSize(headerTableSize);
|
||||
getHpackEncoder().setValidateEncoding(validateEncoding);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setHeaderTableSize(int maxTableSize)
|
||||
{
|
||||
getHpackEncoder().setTableCapacity(maxTableSize);
|
||||
}
|
||||
|
||||
public void setMaxFrameSize(int maxFrameSize)
|
||||
|
@ -91,8 +110,9 @@ public class Generator
|
|||
return dataGenerator.generate(lease, frame, maxLength);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setMaxHeaderListSize(int value)
|
||||
{
|
||||
hpackEncoder.setMaxHeaderListSize(value);
|
||||
getHpackEncoder().setMaxHeaderListSize(value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,9 @@ public class GoAwayBodyParser extends BodyParser
|
|||
{
|
||||
case PREPARE:
|
||||
{
|
||||
// SPEC: wrong streamId is treated as connection error.
|
||||
if (getStreamId() != 0)
|
||||
return connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_go_away_frame");
|
||||
state = State.LAST_STREAM_ID;
|
||||
length = getBodyLength();
|
||||
break;
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
package org.eclipse.jetty.http2.parser;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import org.eclipse.jetty.http2.ErrorCode;
|
||||
import org.eclipse.jetty.http2.Flags;
|
||||
|
@ -44,33 +43,46 @@ public class Parser
|
|||
private static final Logger LOG = LoggerFactory.getLogger(Parser.class);
|
||||
|
||||
private final ByteBufferPool byteBufferPool;
|
||||
private final Listener listener;
|
||||
private final HeaderParser headerParser;
|
||||
private final HpackDecoder hpackDecoder;
|
||||
private final BodyParser[] bodyParsers;
|
||||
private Listener listener;
|
||||
private UnknownBodyParser unknownBodyParser;
|
||||
private int maxFrameLength = Frame.DEFAULT_MAX_LENGTH;
|
||||
private int maxFrameSize = Frame.DEFAULT_MAX_LENGTH;
|
||||
private int maxSettingsKeys = SettingsFrame.DEFAULT_MAX_KEYS;
|
||||
private boolean continuation;
|
||||
private State state = State.HEADER;
|
||||
|
||||
public Parser(ByteBufferPool byteBufferPool, Listener listener, int maxDynamicTableSize, int maxHeaderSize)
|
||||
@Deprecated
|
||||
public Parser(ByteBufferPool byteBufferPool, int maxTableCapacity, int maxHeaderSize)
|
||||
{
|
||||
this(byteBufferPool, listener, maxDynamicTableSize, maxHeaderSize, RateControl.NO_RATE_CONTROL);
|
||||
this(byteBufferPool, maxHeaderSize);
|
||||
}
|
||||
|
||||
public Parser(ByteBufferPool byteBufferPool, Listener listener, int maxDynamicTableSize, int maxHeaderSize, RateControl rateControl)
|
||||
public Parser(ByteBufferPool byteBufferPool, int maxHeaderSize)
|
||||
{
|
||||
this(byteBufferPool, maxHeaderSize, RateControl.NO_RATE_CONTROL);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Parser(ByteBufferPool byteBufferPool, int maxTableSize, int maxHeaderSize, RateControl rateControl)
|
||||
{
|
||||
this(byteBufferPool, maxHeaderSize, rateControl);
|
||||
}
|
||||
|
||||
public Parser(ByteBufferPool byteBufferPool, int maxHeaderSize, RateControl rateControl)
|
||||
{
|
||||
this.byteBufferPool = byteBufferPool;
|
||||
this.listener = listener;
|
||||
this.headerParser = new HeaderParser(rateControl == null ? RateControl.NO_RATE_CONTROL : rateControl);
|
||||
this.hpackDecoder = new HpackDecoder(maxDynamicTableSize, maxHeaderSize);
|
||||
this.hpackDecoder = new HpackDecoder(maxHeaderSize);
|
||||
this.bodyParsers = new BodyParser[FrameType.values().length];
|
||||
}
|
||||
|
||||
public void init(UnaryOperator<Listener> wrapper)
|
||||
public void init(Listener listener)
|
||||
{
|
||||
Listener listener = wrapper.apply(this.listener);
|
||||
if (this.listener != null)
|
||||
throw new IllegalStateException("Invalid parser initialization");
|
||||
this.listener = listener;
|
||||
unknownBodyParser = new UnknownBodyParser(headerParser, listener);
|
||||
HeaderBlockParser headerBlockParser = new HeaderBlockParser(headerParser, byteBufferPool, hpackDecoder, unknownBodyParser);
|
||||
HeaderBlockFragments headerBlockFragments = new HeaderBlockFragments(byteBufferPool);
|
||||
|
@ -86,6 +98,16 @@ public class Parser
|
|||
bodyParsers[FrameType.CONTINUATION.getType()] = new ContinuationBodyParser(headerParser, listener, headerBlockParser, headerBlockFragments);
|
||||
}
|
||||
|
||||
protected Listener getListener()
|
||||
{
|
||||
return listener;
|
||||
}
|
||||
|
||||
public HpackDecoder getHpackDecoder()
|
||||
{
|
||||
return hpackDecoder;
|
||||
}
|
||||
|
||||
private void reset()
|
||||
{
|
||||
headerParser.reset();
|
||||
|
@ -146,7 +168,7 @@ public class Parser
|
|||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Parsed {} frame header from {}@{}", headerParser, buffer, Integer.toHexString(buffer.hashCode()));
|
||||
|
||||
if (headerParser.getLength() > getMaxFrameLength())
|
||||
if (headerParser.getLength() > getMaxFrameSize())
|
||||
return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR, "invalid_frame_length");
|
||||
|
||||
FrameType frameType = FrameType.from(getFrameType());
|
||||
|
@ -214,14 +236,26 @@ public class Parser
|
|||
return headerParser.hasFlag(bit);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public int getMaxFrameLength()
|
||||
{
|
||||
return maxFrameLength;
|
||||
return getMaxFrameSize();
|
||||
}
|
||||
|
||||
public void setMaxFrameLength(int maxFrameLength)
|
||||
@Deprecated
|
||||
public void setMaxFrameLength(int maxFrameSize)
|
||||
{
|
||||
this.maxFrameLength = maxFrameLength;
|
||||
setMaxFrameSize(maxFrameSize);
|
||||
}
|
||||
|
||||
public int getMaxFrameSize()
|
||||
{
|
||||
return maxFrameSize;
|
||||
}
|
||||
|
||||
public void setMaxFrameSize(int maxFrameSize)
|
||||
{
|
||||
this.maxFrameSize = maxFrameSize;
|
||||
}
|
||||
|
||||
public int getMaxSettingsKeys()
|
||||
|
|
|
@ -27,18 +27,34 @@ public class ServerParser extends Parser
|
|||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ServerParser.class);
|
||||
|
||||
private final Listener listener;
|
||||
private final PrefaceParser prefaceParser;
|
||||
private PrefaceParser prefaceParser;
|
||||
private State state = State.PREFACE;
|
||||
private boolean notifyPreface = true;
|
||||
|
||||
public ServerParser(ByteBufferPool byteBufferPool, Listener listener, int maxDynamicTableSize, int maxHeaderSize, RateControl rateControl)
|
||||
@Deprecated
|
||||
public ServerParser(ByteBufferPool byteBufferPool, int maxTableSize, int maxHeaderSize, RateControl rateControl)
|
||||
{
|
||||
super(byteBufferPool, listener, maxDynamicTableSize, maxHeaderSize, rateControl);
|
||||
this.listener = listener;
|
||||
this(byteBufferPool, maxHeaderSize, rateControl);
|
||||
}
|
||||
|
||||
public ServerParser(ByteBufferPool byteBufferPool, int maxHeaderSize, RateControl rateControl)
|
||||
{
|
||||
super(byteBufferPool, maxHeaderSize, rateControl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Parser.Listener listener)
|
||||
{
|
||||
super.init(listener);
|
||||
this.prefaceParser = new PrefaceParser(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Listener getListener()
|
||||
{
|
||||
return (Listener)super.getListener();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>A direct upgrade is an unofficial upgrade from HTTP/1.1 to HTTP/2.0.</p>
|
||||
* <p>A direct upgrade is initiated when {@code org.eclipse.jetty.server.HttpConnection}
|
||||
|
@ -132,6 +148,7 @@ public class ServerParser extends Parser
|
|||
|
||||
private void notifyPreface()
|
||||
{
|
||||
Listener listener = getListener();
|
||||
try
|
||||
{
|
||||
listener.onPreface();
|
||||
|
|
|
@ -68,6 +68,8 @@ public class SettingsBodyParser extends BodyParser
|
|||
@Override
|
||||
protected void emptyBody(ByteBuffer buffer)
|
||||
{
|
||||
if (!validateFrame(buffer, getStreamId(), 0))
|
||||
return;
|
||||
boolean isReply = hasFlag(Flags.ACK);
|
||||
SettingsFrame frame = new SettingsFrame(Collections.emptyMap(), isReply);
|
||||
if (!isReply && !rateControlOnEvent(frame))
|
||||
|
@ -76,6 +78,17 @@ public class SettingsBodyParser extends BodyParser
|
|||
onSettings(frame);
|
||||
}
|
||||
|
||||
private boolean validateFrame(ByteBuffer buffer, int streamId, int bodyLength)
|
||||
{
|
||||
// SPEC: wrong streamId is treated as connection error.
|
||||
if (streamId != 0)
|
||||
return connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_settings_frame");
|
||||
// SPEC: reply with body is treated as connection error.
|
||||
if (hasFlag(Flags.ACK) && bodyLength > 0)
|
||||
return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_settings_frame");
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean parse(ByteBuffer buffer)
|
||||
{
|
||||
|
@ -90,9 +103,8 @@ public class SettingsBodyParser extends BodyParser
|
|||
{
|
||||
case PREPARE:
|
||||
{
|
||||
// SPEC: wrong streamId is treated as connection error.
|
||||
if (streamId != 0)
|
||||
return connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_settings_frame");
|
||||
if (!validateFrame(buffer, streamId, bodyLength))
|
||||
return false;
|
||||
length = bodyLength;
|
||||
settings = new HashMap<>();
|
||||
state = State.SETTING_ID;
|
||||
|
|
|
@ -16,7 +16,6 @@ package org.eclipse.jetty.http2.frames;
|
|||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import org.eclipse.jetty.http.HostPortHttpField;
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
|
@ -46,7 +45,8 @@ public class ContinuationParseTest
|
|||
HeadersGenerator generator = new HeadersGenerator(new HeaderGenerator(), new HpackEncoder());
|
||||
|
||||
final List<HeadersFrame> frames = new ArrayList<>();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onHeaders(HeadersFrame frame)
|
||||
|
@ -59,8 +59,7 @@ public class ContinuationParseTest
|
|||
{
|
||||
frames.add(new HeadersFrame(null, null, false));
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
// Iterate a few times to be sure the parser is properly reset.
|
||||
for (int i = 0; i < 2; ++i)
|
||||
|
|
|
@ -17,7 +17,6 @@ import java.nio.ByteBuffer;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import org.eclipse.jetty.http2.generator.DataGenerator;
|
||||
import org.eclipse.jetty.http2.generator.HeaderGenerator;
|
||||
|
@ -88,15 +87,15 @@ public class DataGenerateParseTest
|
|||
DataGenerator generator = new DataGenerator(new HeaderGenerator());
|
||||
|
||||
final List<DataFrame> frames = new ArrayList<>();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onData(DataFrame frame)
|
||||
{
|
||||
frames.add(frame);
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
// Iterate a few times to be sure generator and parser are properly reset.
|
||||
for (int i = 0; i < 2; ++i)
|
||||
|
@ -128,15 +127,15 @@ public class DataGenerateParseTest
|
|||
DataGenerator generator = new DataGenerator(new HeaderGenerator());
|
||||
|
||||
final List<DataFrame> frames = new ArrayList<>();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onData(DataFrame frame)
|
||||
{
|
||||
frames.add(frame);
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
// Iterate a few times to be sure generator and parser are properly reset.
|
||||
for (int i = 0; i < 2; ++i)
|
||||
|
|
|
@ -16,7 +16,6 @@ package org.eclipse.jetty.http2.frames;
|
|||
import java.nio.ByteBuffer;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.http.MetaData;
|
||||
|
@ -123,15 +122,15 @@ public class FrameFloodTest
|
|||
private void testFrameFlood(byte[] preamble, byte[] bytes)
|
||||
{
|
||||
AtomicBoolean failed = new AtomicBoolean();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192, new WindowRateControl(8, Duration.ofSeconds(1)));
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onConnectionFailure(int error, String reason)
|
||||
{
|
||||
failed.set(true);
|
||||
}
|
||||
}, 4096, 8192, new WindowRateControl(8, Duration.ofSeconds(1)));
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
if (preamble != null)
|
||||
{
|
||||
|
|
|
@ -17,7 +17,6 @@ import java.nio.ByteBuffer;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import org.eclipse.jetty.http2.generator.GoAwayGenerator;
|
||||
import org.eclipse.jetty.http2.generator.HeaderGenerator;
|
||||
|
@ -40,15 +39,15 @@ public class GoAwayGenerateParseTest
|
|||
GoAwayGenerator generator = new GoAwayGenerator(new HeaderGenerator());
|
||||
|
||||
final List<GoAwayFrame> frames = new ArrayList<>();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onGoAway(GoAwayFrame frame)
|
||||
{
|
||||
frames.add(frame);
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
int lastStreamId = 13;
|
||||
int error = 17;
|
||||
|
@ -82,15 +81,15 @@ public class GoAwayGenerateParseTest
|
|||
GoAwayGenerator generator = new GoAwayGenerator(new HeaderGenerator());
|
||||
|
||||
final List<GoAwayFrame> frames = new ArrayList<>();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onGoAway(GoAwayFrame frame)
|
||||
{
|
||||
frames.add(frame);
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
int lastStreamId = 13;
|
||||
int error = 17;
|
||||
|
|
|
@ -16,7 +16,6 @@ package org.eclipse.jetty.http2.frames;
|
|||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import org.eclipse.jetty.http.HostPortHttpField;
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
|
@ -52,15 +51,15 @@ public class HeadersGenerateParseTest
|
|||
MetaData.Request metaData = new MetaData.Request("GET", HttpScheme.HTTP.asString(), new HostPortHttpField("localhost:8080"), "/path", HttpVersion.HTTP_2, fields, -1);
|
||||
|
||||
final List<HeadersFrame> frames = new ArrayList<>();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onHeaders(HeadersFrame frame)
|
||||
{
|
||||
frames.add(frame);
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
// Iterate a few times to be sure generator and parser are properly reset.
|
||||
for (int i = 0; i < 2; ++i)
|
||||
|
@ -105,15 +104,15 @@ public class HeadersGenerateParseTest
|
|||
HeadersGenerator generator = new HeadersGenerator(new HeaderGenerator(), new HpackEncoder());
|
||||
|
||||
final List<HeadersFrame> frames = new ArrayList<>();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onHeaders(HeadersFrame frame)
|
||||
{
|
||||
frames.add(frame);
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
// Iterate a few times to be sure generator and parser are properly reset.
|
||||
for (int i = 0; i < 2; ++i)
|
||||
|
|
|
@ -15,7 +15,6 @@ package org.eclipse.jetty.http2.frames;
|
|||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import org.eclipse.jetty.http.HostPortHttpField;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
|
@ -67,15 +66,15 @@ public class HeadersTooLargeParseTest
|
|||
HeadersGenerator generator = new HeadersGenerator(new HeaderGenerator(), new HpackEncoder());
|
||||
|
||||
AtomicInteger failure = new AtomicInteger();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, maxHeaderSize);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onConnectionFailure(int error, String reason)
|
||||
{
|
||||
failure.set(error);
|
||||
}
|
||||
}, 4096, maxHeaderSize);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
int streamId = 48;
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
|
|
|
@ -15,7 +15,6 @@ package org.eclipse.jetty.http2.frames;
|
|||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import org.eclipse.jetty.http2.ErrorCode;
|
||||
import org.eclipse.jetty.http2.parser.Parser;
|
||||
|
@ -35,16 +34,16 @@ public class MaxFrameSizeParseTest
|
|||
int maxFrameLength = Frame.DEFAULT_MAX_LENGTH + 16;
|
||||
|
||||
AtomicInteger failure = new AtomicInteger();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.setMaxFrameSize(maxFrameLength);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onConnectionFailure(int error, String reason)
|
||||
{
|
||||
failure.set(error);
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.setMaxFrameLength(maxFrameLength);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
// Iterate a few times to be sure the parser is properly reset.
|
||||
for (int i = 0; i < 2; ++i)
|
||||
|
|
|
@ -17,7 +17,6 @@ import java.nio.ByteBuffer;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import org.eclipse.jetty.http2.generator.HeaderGenerator;
|
||||
import org.eclipse.jetty.http2.generator.PingGenerator;
|
||||
|
@ -41,15 +40,15 @@ public class PingGenerateParseTest
|
|||
PingGenerator generator = new PingGenerator(new HeaderGenerator());
|
||||
|
||||
final List<PingFrame> frames = new ArrayList<>();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onPing(PingFrame frame)
|
||||
{
|
||||
frames.add(frame);
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
byte[] payload = new byte[8];
|
||||
new Random().nextBytes(payload);
|
||||
|
@ -82,15 +81,15 @@ public class PingGenerateParseTest
|
|||
PingGenerator generator = new PingGenerator(new HeaderGenerator());
|
||||
|
||||
final List<PingFrame> frames = new ArrayList<>();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onPing(PingFrame frame)
|
||||
{
|
||||
frames.add(frame);
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
byte[] payload = new byte[8];
|
||||
new Random().nextBytes(payload);
|
||||
|
@ -123,15 +122,15 @@ public class PingGenerateParseTest
|
|||
PingGenerator generator = new PingGenerator(new HeaderGenerator());
|
||||
|
||||
final List<PingFrame> frames = new ArrayList<>();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onPing(PingFrame frame)
|
||||
{
|
||||
frames.add(frame);
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
PingFrame ping = new PingFrame(NanoTime.now(), true);
|
||||
|
|
|
@ -16,7 +16,6 @@ package org.eclipse.jetty.http2.frames;
|
|||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import org.eclipse.jetty.http2.generator.HeaderGenerator;
|
||||
import org.eclipse.jetty.http2.generator.PriorityGenerator;
|
||||
|
@ -37,15 +36,15 @@ public class PriorityGenerateParseTest
|
|||
PriorityGenerator generator = new PriorityGenerator(new HeaderGenerator());
|
||||
|
||||
final List<PriorityFrame> frames = new ArrayList<>();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onPriority(PriorityFrame frame)
|
||||
{
|
||||
frames.add(frame);
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
int streamId = 13;
|
||||
int parentStreamId = 17;
|
||||
|
@ -82,15 +81,15 @@ public class PriorityGenerateParseTest
|
|||
PriorityGenerator generator = new PriorityGenerator(new HeaderGenerator());
|
||||
|
||||
final List<PriorityFrame> frames = new ArrayList<>();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onPriority(PriorityFrame frame)
|
||||
{
|
||||
frames.add(frame);
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
int streamId = 13;
|
||||
int parentStreamId = 17;
|
||||
|
|
|
@ -16,7 +16,6 @@ package org.eclipse.jetty.http2.frames;
|
|||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import org.eclipse.jetty.http.HostPortHttpField;
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
|
@ -45,15 +44,15 @@ public class PushPromiseGenerateParseTest
|
|||
PushPromiseGenerator generator = new PushPromiseGenerator(new HeaderGenerator(), new HpackEncoder());
|
||||
|
||||
final List<PushPromiseFrame> frames = new ArrayList<>();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onPushPromise(PushPromiseFrame frame)
|
||||
{
|
||||
frames.add(frame);
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
int streamId = 13;
|
||||
int promisedStreamId = 17;
|
||||
|
@ -98,15 +97,15 @@ public class PushPromiseGenerateParseTest
|
|||
PushPromiseGenerator generator = new PushPromiseGenerator(new HeaderGenerator(), new HpackEncoder());
|
||||
|
||||
final List<PushPromiseFrame> frames = new ArrayList<>();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onPushPromise(PushPromiseFrame frame)
|
||||
{
|
||||
frames.add(frame);
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
int streamId = 13;
|
||||
int promisedStreamId = 17;
|
||||
|
|
|
@ -16,7 +16,6 @@ package org.eclipse.jetty.http2.frames;
|
|||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import org.eclipse.jetty.http2.generator.HeaderGenerator;
|
||||
import org.eclipse.jetty.http2.generator.ResetGenerator;
|
||||
|
@ -37,15 +36,15 @@ public class ResetGenerateParseTest
|
|||
ResetGenerator generator = new ResetGenerator(new HeaderGenerator());
|
||||
|
||||
final List<ResetFrame> frames = new ArrayList<>();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onReset(ResetFrame frame)
|
||||
{
|
||||
frames.add(frame);
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
int streamId = 13;
|
||||
int error = 17;
|
||||
|
@ -78,15 +77,15 @@ public class ResetGenerateParseTest
|
|||
ResetGenerator generator = new ResetGenerator(new HeaderGenerator());
|
||||
|
||||
final List<ResetFrame> frames = new ArrayList<>();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onReset(ResetFrame frame)
|
||||
{
|
||||
frames.add(frame);
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
int streamId = 13;
|
||||
int error = 17;
|
||||
|
|
|
@ -20,7 +20,6 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import org.eclipse.jetty.http2.ErrorCode;
|
||||
import org.eclipse.jetty.http2.generator.HeaderGenerator;
|
||||
|
@ -31,6 +30,7 @@ import org.eclipse.jetty.io.MappedByteBufferPool;
|
|||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class SettingsGenerateParseTest
|
||||
|
@ -40,8 +40,7 @@ public class SettingsGenerateParseTest
|
|||
@Test
|
||||
public void testGenerateParseNoSettings()
|
||||
{
|
||||
|
||||
List<SettingsFrame> frames = testGenerateParse(Collections.<Integer, Integer>emptyMap());
|
||||
List<SettingsFrame> frames = testGenerateParse(Collections.emptyMap(), true);
|
||||
assertEquals(1, frames.size());
|
||||
SettingsFrame frame = frames.get(0);
|
||||
assertEquals(0, frame.getSettings().size());
|
||||
|
@ -58,7 +57,7 @@ public class SettingsGenerateParseTest
|
|||
int key2 = 19;
|
||||
Integer value2 = 23;
|
||||
settings1.put(key2, value2);
|
||||
List<SettingsFrame> frames = testGenerateParse(settings1);
|
||||
List<SettingsFrame> frames = testGenerateParse(settings1, false);
|
||||
assertEquals(1, frames.size());
|
||||
SettingsFrame frame = frames.get(0);
|
||||
Map<Integer, Integer> settings2 = frame.getSettings();
|
||||
|
@ -67,26 +66,26 @@ public class SettingsGenerateParseTest
|
|||
assertEquals(value2, settings2.get(key2));
|
||||
}
|
||||
|
||||
private List<SettingsFrame> testGenerateParse(Map<Integer, Integer> settings)
|
||||
private List<SettingsFrame> testGenerateParse(Map<Integer, Integer> settings, boolean reply)
|
||||
{
|
||||
SettingsGenerator generator = new SettingsGenerator(new HeaderGenerator());
|
||||
|
||||
List<SettingsFrame> frames = new ArrayList<>();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onSettings(SettingsFrame frame)
|
||||
{
|
||||
frames.add(frame);
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
// Iterate a few times to be sure generator and parser are properly reset.
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generateSettings(lease, settings, true);
|
||||
generator.generateSettings(lease, settings, reply);
|
||||
|
||||
frames.clear();
|
||||
for (ByteBuffer buffer : lease.getByteBuffers())
|
||||
|
@ -107,20 +106,20 @@ public class SettingsGenerateParseTest
|
|||
SettingsGenerator generator = new SettingsGenerator(new HeaderGenerator());
|
||||
|
||||
AtomicInteger errorRef = new AtomicInteger();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onConnectionFailure(int error, String reason)
|
||||
{
|
||||
errorRef.set(error);
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
Map<Integer, Integer> settings1 = new HashMap<>();
|
||||
settings1.put(13, 17);
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generateSettings(lease, settings1, true);
|
||||
generator.generateSettings(lease, settings1, false);
|
||||
// Modify the length of the frame to make it invalid
|
||||
ByteBuffer bytes = lease.getByteBuffers().get(0);
|
||||
bytes.putShort(1, (short)(bytes.getShort(1) - 1));
|
||||
|
@ -142,15 +141,15 @@ public class SettingsGenerateParseTest
|
|||
SettingsGenerator generator = new SettingsGenerator(new HeaderGenerator());
|
||||
|
||||
List<SettingsFrame> frames = new ArrayList<>();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onSettings(SettingsFrame frame)
|
||||
{
|
||||
frames.add(frame);
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
Map<Integer, Integer> settings1 = new HashMap<>();
|
||||
int key = 13;
|
||||
|
@ -161,7 +160,7 @@ public class SettingsGenerateParseTest
|
|||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generateSettings(lease, settings1, true);
|
||||
generator.generateSettings(lease, settings1, false);
|
||||
|
||||
frames.clear();
|
||||
for (ByteBuffer buffer : lease.getByteBuffers())
|
||||
|
@ -177,7 +176,7 @@ public class SettingsGenerateParseTest
|
|||
Map<Integer, Integer> settings2 = frame.getSettings();
|
||||
assertEquals(1, settings2.size());
|
||||
assertEquals(value, settings2.get(key));
|
||||
assertTrue(frame.isReply());
|
||||
assertFalse(frame.isReply());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -187,17 +186,17 @@ public class SettingsGenerateParseTest
|
|||
SettingsGenerator generator = new SettingsGenerator(new HeaderGenerator());
|
||||
|
||||
AtomicInteger errorRef = new AtomicInteger();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
int maxSettingsKeys = 32;
|
||||
parser.setMaxSettingsKeys(maxSettingsKeys);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onConnectionFailure(int error, String reason)
|
||||
{
|
||||
errorRef.set(error);
|
||||
}
|
||||
}, 4096, 8192);
|
||||
int maxSettingsKeys = 32;
|
||||
parser.setMaxSettingsKeys(maxSettingsKeys);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
Map<Integer, Integer> settings = new HashMap<>();
|
||||
for (int i = 0; i < maxSettingsKeys + 1; ++i)
|
||||
|
@ -227,10 +226,10 @@ public class SettingsGenerateParseTest
|
|||
int maxSettingsKeys = pairs / 2;
|
||||
|
||||
AtomicInteger errorRef = new AtomicInteger();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter(), 4096, 8192);
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.setMaxSettingsKeys(maxSettingsKeys);
|
||||
parser.setMaxFrameLength(Frame.DEFAULT_MAX_LENGTH);
|
||||
parser.init(listener -> new Parser.Listener.Wrapper(listener)
|
||||
parser.setMaxFrameSize(Frame.DEFAULT_MAX_LENGTH);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onConnectionFailure(int error, String reason)
|
||||
|
@ -268,17 +267,17 @@ public class SettingsGenerateParseTest
|
|||
SettingsGenerator generator = new SettingsGenerator(new HeaderGenerator());
|
||||
|
||||
AtomicInteger errorRef = new AtomicInteger();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
int maxSettingsKeys = 32;
|
||||
parser.setMaxSettingsKeys(maxSettingsKeys);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onConnectionFailure(int error, String reason)
|
||||
{
|
||||
errorRef.set(error);
|
||||
}
|
||||
}, 4096, 8192);
|
||||
int maxSettingsKeys = 32;
|
||||
parser.setMaxSettingsKeys(maxSettingsKeys);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
Map<Integer, Integer> settings = new HashMap<>();
|
||||
settings.put(13, 17);
|
||||
|
|
|
@ -17,7 +17,6 @@ import java.nio.ByteBuffer;
|
|||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import org.eclipse.jetty.http2.ErrorCode;
|
||||
import org.eclipse.jetty.http2.parser.Parser;
|
||||
|
@ -48,8 +47,8 @@ public class UnknownParseTest
|
|||
public void testInvalidFrameSize()
|
||||
{
|
||||
AtomicInteger failure = new AtomicInteger();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter(), 4096, 8192);
|
||||
parser.init(listener -> new Parser.Listener.Wrapper(listener)
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onConnectionFailure(int error, String reason)
|
||||
|
@ -57,7 +56,7 @@ public class UnknownParseTest
|
|||
failure.set(error);
|
||||
}
|
||||
});
|
||||
parser.setMaxFrameLength(Frame.DEFAULT_MAX_LENGTH);
|
||||
parser.setMaxFrameSize(Frame.DEFAULT_MAX_LENGTH);
|
||||
|
||||
// 0x4001 == 16385 which is > Frame.DEFAULT_MAX_LENGTH.
|
||||
byte[] bytes = new byte[]{0, 0x40, 0x01, 64, 0, 0, 0, 0, 0};
|
||||
|
@ -73,15 +72,15 @@ public class UnknownParseTest
|
|||
private void testParse(Function<ByteBuffer, ByteBuffer> fn)
|
||||
{
|
||||
AtomicBoolean failure = new AtomicBoolean();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onConnectionFailure(int error, String reason)
|
||||
{
|
||||
failure.set(true);
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
// Iterate a few times to be sure the parser is properly reset.
|
||||
for (int i = 0; i < 2; ++i)
|
||||
|
|
|
@ -16,7 +16,6 @@ package org.eclipse.jetty.http2.frames;
|
|||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import org.eclipse.jetty.http2.generator.HeaderGenerator;
|
||||
import org.eclipse.jetty.http2.generator.WindowUpdateGenerator;
|
||||
|
@ -37,15 +36,15 @@ public class WindowUpdateGenerateParseTest
|
|||
WindowUpdateGenerator generator = new WindowUpdateGenerator(new HeaderGenerator());
|
||||
|
||||
final List<WindowUpdateFrame> frames = new ArrayList<>();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onWindowUpdate(WindowUpdateFrame frame)
|
||||
{
|
||||
frames.add(frame);
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
int streamId = 13;
|
||||
int windowUpdate = 17;
|
||||
|
@ -78,15 +77,15 @@ public class WindowUpdateGenerateParseTest
|
|||
WindowUpdateGenerator generator = new WindowUpdateGenerator(new HeaderGenerator());
|
||||
|
||||
final List<WindowUpdateFrame> frames = new ArrayList<>();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onWindowUpdate(WindowUpdateFrame frame)
|
||||
{
|
||||
frames.add(frame);
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
int streamId = 13;
|
||||
int windowUpdate = 17;
|
||||
|
|
|
@ -41,7 +41,7 @@ import org.slf4j.LoggerFactory;
|
|||
*/
|
||||
public class HpackContext
|
||||
{
|
||||
public static final Logger LOG = LoggerFactory.getLogger(HpackContext.class);
|
||||
private static final Logger LOG = LoggerFactory.getLogger(HpackContext.class);
|
||||
private static final String EMPTY = "";
|
||||
public static final String[][] STATIC_TABLE =
|
||||
{
|
||||
|
@ -114,6 +114,7 @@ public class HpackContext
|
|||
private static final StaticEntry[] __staticTableByHeader = new StaticEntry[HttpHeader.values().length];
|
||||
private static final StaticEntry[] __staticTable = new StaticEntry[STATIC_TABLE.length];
|
||||
public static final int STATIC_SIZE = STATIC_TABLE.length - 1;
|
||||
public static final int DEFAULT_MAX_TABLE_CAPACITY = 4096;
|
||||
|
||||
static
|
||||
{
|
||||
|
@ -183,26 +184,26 @@ public class HpackContext
|
|||
}
|
||||
}
|
||||
|
||||
private int _maxDynamicTableSizeInBytes;
|
||||
private int _dynamicTableSizeInBytes;
|
||||
private final DynamicTable _dynamicTable;
|
||||
private final Map<HttpField, Entry> _fieldMap = new HashMap<>();
|
||||
private final Map<String, Entry> _nameMap = new HashMap<>();
|
||||
private int _maxTableSize;
|
||||
private int _tableSize;
|
||||
|
||||
HpackContext(int maxDynamicTableSize)
|
||||
HpackContext(int maxTableSize)
|
||||
{
|
||||
_maxDynamicTableSizeInBytes = maxDynamicTableSize;
|
||||
int guesstimateEntries = 10 + maxDynamicTableSize / (32 + 10 + 10);
|
||||
_maxTableSize = maxTableSize;
|
||||
int guesstimateEntries = 10 + maxTableSize / (32 + 10 + 10);
|
||||
_dynamicTable = new DynamicTable(guesstimateEntries);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug(String.format("HdrTbl[%x] created max=%d", hashCode(), maxDynamicTableSize));
|
||||
LOG.debug(String.format("HdrTbl[%x] created max=%d", hashCode(), maxTableSize));
|
||||
}
|
||||
|
||||
public void resize(int newMaxDynamicTableSize)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug(String.format("HdrTbl[%x] resized max=%d->%d", hashCode(), _maxDynamicTableSizeInBytes, newMaxDynamicTableSize));
|
||||
_maxDynamicTableSizeInBytes = newMaxDynamicTableSize;
|
||||
LOG.debug(String.format("HdrTbl[%x] resized max=%d->%d", hashCode(), _maxTableSize, newMaxDynamicTableSize));
|
||||
_maxTableSize = newMaxDynamicTableSize;
|
||||
_dynamicTable.evict();
|
||||
}
|
||||
|
||||
|
@ -247,14 +248,14 @@ public class HpackContext
|
|||
{
|
||||
Entry entry = new Entry(field);
|
||||
int size = entry.getSize();
|
||||
if (size > _maxDynamicTableSizeInBytes)
|
||||
if (size > _maxTableSize)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug(String.format("HdrTbl[%x] !added size %d>%d", hashCode(), size, _maxDynamicTableSizeInBytes));
|
||||
LOG.debug(String.format("HdrTbl[%x] !added size %d>%d", hashCode(), size, _maxTableSize));
|
||||
_dynamicTable.evictAll();
|
||||
return null;
|
||||
}
|
||||
_dynamicTableSizeInBytes += size;
|
||||
_tableSize += size;
|
||||
_dynamicTable.add(entry);
|
||||
_fieldMap.put(field, entry);
|
||||
_nameMap.put(field.getLowerCaseName(), entry);
|
||||
|
@ -278,7 +279,7 @@ public class HpackContext
|
|||
*/
|
||||
public int getDynamicTableSize()
|
||||
{
|
||||
return _dynamicTableSizeInBytes;
|
||||
return _tableSize;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -286,7 +287,7 @@ public class HpackContext
|
|||
*/
|
||||
public int getMaxDynamicTableSize()
|
||||
{
|
||||
return _maxDynamicTableSizeInBytes;
|
||||
return _maxTableSize;
|
||||
}
|
||||
|
||||
public int index(Entry entry)
|
||||
|
@ -312,15 +313,15 @@ public class HpackContext
|
|||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("HpackContext@%x{entries=%d,size=%d,max=%d}", hashCode(), _dynamicTable.size(), _dynamicTableSizeInBytes, _maxDynamicTableSizeInBytes);
|
||||
return String.format("HpackContext@%x{entries=%d,size=%d,max=%d}", hashCode(), _dynamicTable.size(), _tableSize, _maxTableSize);
|
||||
}
|
||||
|
||||
private class DynamicTable
|
||||
{
|
||||
Entry[] _entries;
|
||||
int _size;
|
||||
int _offset;
|
||||
int _growby;
|
||||
private Entry[] _entries;
|
||||
private final int _growby;
|
||||
private int _size;
|
||||
private int _offset;
|
||||
|
||||
private DynamicTable(int initCapacity)
|
||||
{
|
||||
|
@ -368,7 +369,7 @@ public class HpackContext
|
|||
|
||||
private void evict()
|
||||
{
|
||||
while (_dynamicTableSizeInBytes > _maxDynamicTableSizeInBytes)
|
||||
while (_tableSize > _maxTableSize)
|
||||
{
|
||||
Entry entry = _entries[_offset];
|
||||
_entries[_offset] = null;
|
||||
|
@ -376,7 +377,7 @@ public class HpackContext
|
|||
_size--;
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug(String.format("HdrTbl[%x] evict %s", HpackContext.this.hashCode(), entry));
|
||||
_dynamicTableSizeInBytes -= entry.getSize();
|
||||
_tableSize -= entry.getSize();
|
||||
entry._slot = -1;
|
||||
_fieldMap.remove(entry.getHttpField());
|
||||
String lc = entry.getHttpField().getLowerCaseName();
|
||||
|
@ -384,7 +385,7 @@ public class HpackContext
|
|||
_nameMap.remove(lc);
|
||||
}
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug(String.format("HdrTbl[%x] entries=%d, size=%d, max=%d", HpackContext.this.hashCode(), _dynamicTable.size(), _dynamicTableSizeInBytes, _maxDynamicTableSizeInBytes));
|
||||
LOG.debug(String.format("HdrTbl[%x] entries=%d, size=%d, max=%d", HpackContext.this.hashCode(), _dynamicTable.size(), _tableSize, _maxTableSize));
|
||||
}
|
||||
|
||||
private void evictAll()
|
||||
|
@ -397,7 +398,7 @@ public class HpackContext
|
|||
_nameMap.clear();
|
||||
_offset = 0;
|
||||
_size = 0;
|
||||
_dynamicTableSizeInBytes = 0;
|
||||
_tableSize = 0;
|
||||
Arrays.fill(_entries, null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,19 +42,19 @@ public class HpackDecoder
|
|||
private final MetaDataBuilder _builder;
|
||||
private final HuffmanDecoder _huffmanDecoder;
|
||||
private final NBitIntegerDecoder _integerDecoder;
|
||||
private int _localMaxDynamicTableSize;
|
||||
private int _maxTableCapacity;
|
||||
|
||||
/**
|
||||
* @param localMaxDynamicTableSize The maximum allowed size of the local dynamic header field table.
|
||||
* @param maxHeaderSize The maximum allowed size of a headers block, expressed as total of all name and value characters, plus 32 per field
|
||||
* @param maxHeaderSize The maximum allowed size of a decoded headers block,
|
||||
* expressed as total of all name and value bytes, plus 32 bytes per field
|
||||
*/
|
||||
public HpackDecoder(int localMaxDynamicTableSize, int maxHeaderSize)
|
||||
public HpackDecoder(int maxHeaderSize)
|
||||
{
|
||||
_context = new HpackContext(localMaxDynamicTableSize);
|
||||
_localMaxDynamicTableSize = localMaxDynamicTableSize;
|
||||
_context = new HpackContext(0);
|
||||
_builder = new MetaDataBuilder(maxHeaderSize);
|
||||
_huffmanDecoder = new HuffmanDecoder();
|
||||
_integerDecoder = new NBitIntegerDecoder();
|
||||
setMaxTableCapacity(HpackContext.DEFAULT_MAX_TABLE_CAPACITY);
|
||||
}
|
||||
|
||||
public HpackContext getHpackContext()
|
||||
|
@ -62,9 +62,39 @@ public class HpackDecoder
|
|||
return _context;
|
||||
}
|
||||
|
||||
public void setLocalMaxDynamicTableSize(int localMaxdynamciTableSize)
|
||||
public int getMaxTableCapacity()
|
||||
{
|
||||
_localMaxDynamicTableSize = localMaxdynamciTableSize;
|
||||
return _maxTableCapacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the limit for the capacity of the dynamic header table.</p>
|
||||
* <p>This value acts as a limit for the values received from the
|
||||
* remote peer via the HPACK dynamic table size update instruction.</p>
|
||||
* <p>After calling this method, a SETTINGS frame must be sent to the other
|
||||
* peer, containing the {@code SETTINGS_HEADER_TABLE_SIZE} setting with
|
||||
* the value passed as argument to this method.</p>
|
||||
*
|
||||
* @param maxTableCapacity the limit for capacity of the dynamic header table
|
||||
*/
|
||||
public void setMaxTableCapacity(int maxTableCapacity)
|
||||
{
|
||||
_maxTableCapacity = maxTableCapacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param maxTableSizeLimit the local dynamic table max size
|
||||
* @deprecated use {@link #setMaxTableCapacity(int)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public void setLocalMaxDynamicTableSize(int maxTableSizeLimit)
|
||||
{
|
||||
setMaxTableCapacity(maxTableSizeLimit);
|
||||
}
|
||||
|
||||
public void setMaxHeaderListSize(int maxHeaderListSize)
|
||||
{
|
||||
_builder.setMaxSize(maxHeaderListSize);
|
||||
}
|
||||
|
||||
public MetaData decode(ByteBuffer buffer) throws HpackException.SessionException, HpackException.StreamException
|
||||
|
@ -72,13 +102,12 @@ public class HpackDecoder
|
|||
if (LOG.isDebugEnabled())
|
||||
LOG.debug(String.format("CtxTbl[%x] decoding %d octets", _context.hashCode(), buffer.remaining()));
|
||||
|
||||
// If the buffer is big, don't even think about decoding it.
|
||||
// Huffman may double the size, but it will only be a temporary allocation until detected in MetaDataBuilder.emit().
|
||||
if (buffer.remaining() > _builder.getMaxSize())
|
||||
throw new HpackException.SessionException("431 Request Header Fields too large");
|
||||
// If the buffer is larger than the max headers size, don't even start decoding it.
|
||||
int maxSize = _builder.getMaxSize();
|
||||
if (maxSize > 0 && buffer.remaining() > maxSize)
|
||||
throw new HpackException.SessionException("Header fields size too large");
|
||||
|
||||
boolean emitted = false;
|
||||
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
|
@ -132,8 +161,8 @@ public class HpackDecoder
|
|||
int size = integerDecode(buffer, 5);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("decode resize={}", size);
|
||||
if (size > _localMaxDynamicTableSize)
|
||||
throw new IllegalArgumentException();
|
||||
if (size > getMaxTableCapacity())
|
||||
throw new HpackException.CompressionException("Dynamic table resize exceeded max limit");
|
||||
if (emitted)
|
||||
throw new HpackException.CompressionException("Dynamic table resize after fields");
|
||||
_context.resize(size);
|
||||
|
|
|
@ -94,34 +94,57 @@ public class HpackEncoder
|
|||
|
||||
private final HpackContext _context;
|
||||
private final boolean _debug;
|
||||
private int _remoteMaxDynamicTableSize;
|
||||
private int _localMaxDynamicTableSize;
|
||||
private int _maxTableCapacity;
|
||||
private int _tableCapacity;
|
||||
private int _maxHeaderListSize;
|
||||
private int _headerListSize;
|
||||
private boolean _validateEncoding = true;
|
||||
|
||||
public HpackEncoder()
|
||||
{
|
||||
this(4096, 4096, -1);
|
||||
}
|
||||
|
||||
public HpackEncoder(int localMaxDynamicTableSize)
|
||||
{
|
||||
this(localMaxDynamicTableSize, 4096, -1);
|
||||
}
|
||||
|
||||
public HpackEncoder(int localMaxDynamicTableSize, int remoteMaxDynamicTableSize)
|
||||
{
|
||||
this(localMaxDynamicTableSize, remoteMaxDynamicTableSize, -1);
|
||||
}
|
||||
|
||||
public HpackEncoder(int localMaxDynamicTableSize, int remoteMaxDynamicTableSize, int maxHeaderListSize)
|
||||
{
|
||||
_context = new HpackContext(remoteMaxDynamicTableSize);
|
||||
_remoteMaxDynamicTableSize = remoteMaxDynamicTableSize;
|
||||
_localMaxDynamicTableSize = localMaxDynamicTableSize;
|
||||
_maxHeaderListSize = maxHeaderListSize;
|
||||
_context = new HpackContext(0);
|
||||
_debug = LOG.isDebugEnabled();
|
||||
setMaxTableCapacity(HpackContext.DEFAULT_MAX_TABLE_CAPACITY);
|
||||
setTableCapacity(HpackContext.DEFAULT_MAX_TABLE_CAPACITY);
|
||||
}
|
||||
|
||||
public int getMaxTableCapacity()
|
||||
{
|
||||
return _maxTableCapacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the limit for the capacity of the dynamic header table.</p>
|
||||
* <p>This value is set by the remote peer via the
|
||||
* {@code SETTINGS_HEADER_TABLE_SIZE} setting.</p>
|
||||
*
|
||||
* @param maxTableSizeLimit the limit for capacity of the dynamic header table
|
||||
*/
|
||||
public void setMaxTableCapacity(int maxTableSizeLimit)
|
||||
{
|
||||
_maxTableCapacity = maxTableSizeLimit;
|
||||
}
|
||||
|
||||
public int getTableCapacity()
|
||||
{
|
||||
return _tableCapacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the capacity of the dynamic header table.</p>
|
||||
* <p>The value of the capacity may be changed from {@code 0}
|
||||
* up to {@link #getMaxTableCapacity()}.
|
||||
* An HPACK instruction with the new capacity value will
|
||||
* be sent to the decoder when the next call to
|
||||
* {@link #encode(ByteBuffer, MetaData)} is made.</p>
|
||||
*
|
||||
* @param tableCapacity the capacity of the dynamic header table
|
||||
*/
|
||||
public void setTableCapacity(int tableCapacity)
|
||||
{
|
||||
if (tableCapacity > getMaxTableCapacity())
|
||||
throw new IllegalArgumentException("Max table capacity exceeded");
|
||||
_tableCapacity = tableCapacity;
|
||||
}
|
||||
|
||||
public int getMaxHeaderListSize()
|
||||
|
@ -139,14 +162,16 @@ public class HpackEncoder
|
|||
return _context;
|
||||
}
|
||||
|
||||
public void setRemoteMaxDynamicTableSize(int remoteMaxDynamicTableSize)
|
||||
@Deprecated
|
||||
public void setRemoteMaxDynamicTableSize(int maxTableSize)
|
||||
{
|
||||
_remoteMaxDynamicTableSize = remoteMaxDynamicTableSize;
|
||||
setTableCapacity(maxTableSize);
|
||||
}
|
||||
|
||||
public void setLocalMaxDynamicTableSize(int localMaxDynamicTableSize)
|
||||
@Deprecated
|
||||
public void setLocalMaxDynamicTableSize(int maxTableSizeLimit)
|
||||
{
|
||||
_localMaxDynamicTableSize = localMaxDynamicTableSize;
|
||||
setMaxTableCapacity(maxTableSizeLimit);
|
||||
}
|
||||
|
||||
public boolean isValidateEncoding()
|
||||
|
@ -182,10 +207,10 @@ public class HpackEncoder
|
|||
_headerListSize = 0;
|
||||
int pos = buffer.position();
|
||||
|
||||
// Check the dynamic table sizes!
|
||||
int maxDynamicTableSize = Math.min(_remoteMaxDynamicTableSize, _localMaxDynamicTableSize);
|
||||
if (maxDynamicTableSize != _context.getMaxDynamicTableSize())
|
||||
encodeMaxDynamicTableSize(buffer, maxDynamicTableSize);
|
||||
// If max table size changed, send the correspondent instruction.
|
||||
int tableCapacity = getTableCapacity();
|
||||
if (tableCapacity != _context.getMaxDynamicTableSize())
|
||||
encodeMaxDynamicTableSize(buffer, tableCapacity);
|
||||
|
||||
// Add Request/response meta fields
|
||||
if (metadata.isRequest())
|
||||
|
@ -259,14 +284,9 @@ public class HpackEncoder
|
|||
}
|
||||
}
|
||||
|
||||
// Check size
|
||||
if (_maxHeaderListSize > 0 && _headerListSize > _maxHeaderListSize)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.warn("Header list size too large {} > {} metadata={}", _headerListSize, _maxHeaderListSize, metadata);
|
||||
else
|
||||
LOG.warn("Header list size too large {} > {}", _headerListSize, _maxHeaderListSize);
|
||||
}
|
||||
int maxHeaderListSize = getMaxHeaderListSize();
|
||||
if (maxHeaderListSize > 0 && _headerListSize > maxHeaderListSize)
|
||||
throw new HpackException.SessionException("Header size %d > %d", _headerListSize, maxHeaderListSize);
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug(String.format("CtxTbl[%x] encoded %d octets", _context.hashCode(), buffer.position() - pos));
|
||||
|
@ -283,13 +303,11 @@ public class HpackEncoder
|
|||
}
|
||||
}
|
||||
|
||||
public void encodeMaxDynamicTableSize(ByteBuffer buffer, int maxDynamicTableSize)
|
||||
public void encodeMaxDynamicTableSize(ByteBuffer buffer, int maxTableSize)
|
||||
{
|
||||
if (maxDynamicTableSize > _remoteMaxDynamicTableSize)
|
||||
throw new IllegalArgumentException();
|
||||
buffer.put((byte)0x20);
|
||||
NBitIntegerEncoder.encode(buffer, 5, maxDynamicTableSize);
|
||||
_context.resize(maxDynamicTableSize);
|
||||
NBitIntegerEncoder.encode(buffer, 5, maxTableSize);
|
||||
_context.resize(maxTableSize);
|
||||
}
|
||||
|
||||
public void encode(ByteBuffer buffer, HttpField field)
|
||||
|
|
|
@ -25,8 +25,8 @@ import org.eclipse.jetty.http2.hpack.HpackException.SessionException;
|
|||
|
||||
public class MetaDataBuilder
|
||||
{
|
||||
private final int _maxSize;
|
||||
private final HttpFields.Mutable _fields = HttpFields.build();
|
||||
private int _maxSize;
|
||||
private int _size;
|
||||
private Integer _status;
|
||||
private String _method;
|
||||
|
@ -48,8 +48,6 @@ public class MetaDataBuilder
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the maxSize.
|
||||
*
|
||||
* @return the maxSize
|
||||
*/
|
||||
public int getMaxSize()
|
||||
|
@ -57,6 +55,11 @@ public class MetaDataBuilder
|
|||
return _maxSize;
|
||||
}
|
||||
|
||||
public void setMaxSize(int maxSize)
|
||||
{
|
||||
_maxSize = maxSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size.
|
||||
*
|
||||
|
@ -76,8 +79,9 @@ public class MetaDataBuilder
|
|||
String value = field.getValue();
|
||||
int fieldSize = name.length() + (value == null ? 0 : value.length());
|
||||
_size += fieldSize + 32;
|
||||
if (_size > _maxSize)
|
||||
throw new SessionException("Header size %d > %d", _size, _maxSize);
|
||||
int maxSize = getMaxSize();
|
||||
if (maxSize > 0 && _size > maxSize)
|
||||
throw new SessionException("Header size %d > %d", _size, maxSize);
|
||||
|
||||
if (field instanceof StaticTableHttpField)
|
||||
{
|
||||
|
|
|
@ -23,7 +23,7 @@ import org.eclipse.jetty.http.MetaData;
|
|||
import org.eclipse.jetty.http2.hpack.HpackException.CompressionException;
|
||||
import org.eclipse.jetty.http2.hpack.HpackException.SessionException;
|
||||
import org.eclipse.jetty.http2.hpack.HpackException.StreamException;
|
||||
import org.eclipse.jetty.util.TypeUtil;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
@ -57,11 +57,13 @@ public class HpackDecoderTest
|
|||
@Test
|
||||
public void testDecodeD3() throws Exception
|
||||
{
|
||||
HpackDecoder decoder = new HpackDecoder(4096, 8192);
|
||||
HpackDecoder decoder = new HpackDecoder(8192);
|
||||
decoder.setMaxTableCapacity(HpackContext.DEFAULT_MAX_TABLE_CAPACITY);
|
||||
decoder.getHpackContext().resize(decoder.getMaxTableCapacity());
|
||||
|
||||
// First request
|
||||
String encoded = "828684410f7777772e6578616d706c652e636f6d";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
|
||||
|
||||
MetaData.Request request = (MetaData.Request)decoder.decode(buffer);
|
||||
|
||||
|
@ -73,7 +75,7 @@ public class HpackDecoderTest
|
|||
|
||||
// Second request
|
||||
encoded = "828684be58086e6f2d6361636865";
|
||||
buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
|
||||
|
||||
request = (MetaData.Request)decoder.decode(buffer);
|
||||
|
||||
|
@ -88,7 +90,7 @@ public class HpackDecoderTest
|
|||
|
||||
// Third request
|
||||
encoded = "828785bf400a637573746f6d2d6b65790c637573746f6d2d76616c7565";
|
||||
buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
|
||||
|
||||
request = (MetaData.Request)decoder.decode(buffer);
|
||||
|
||||
|
@ -105,11 +107,13 @@ public class HpackDecoderTest
|
|||
@Test
|
||||
public void testDecodeD4() throws Exception
|
||||
{
|
||||
HpackDecoder decoder = new HpackDecoder(4096, 8192);
|
||||
HpackDecoder decoder = new HpackDecoder(8192);
|
||||
decoder.setMaxTableCapacity(HpackContext.DEFAULT_MAX_TABLE_CAPACITY);
|
||||
decoder.getHpackContext().resize(decoder.getMaxTableCapacity());
|
||||
|
||||
// First request
|
||||
String encoded = "828684418cf1e3c2e5f23a6ba0ab90f4ff";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
|
||||
|
||||
MetaData.Request request = (MetaData.Request)decoder.decode(buffer);
|
||||
|
||||
|
@ -121,7 +125,7 @@ public class HpackDecoderTest
|
|||
|
||||
// Second request
|
||||
encoded = "828684be5886a8eb10649cbf";
|
||||
buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
|
||||
|
||||
request = (MetaData.Request)decoder.decode(buffer);
|
||||
|
||||
|
@ -140,9 +144,9 @@ public class HpackDecoderTest
|
|||
{
|
||||
String value = "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==";
|
||||
|
||||
HpackDecoder decoder = new HpackDecoder(4096, 8192);
|
||||
HpackDecoder decoder = new HpackDecoder(8192);
|
||||
String encoded = "8682418cF1E3C2E5F23a6bA0Ab90F4Ff841f0822426173696320515778685a475270626a70766347567549484e6c633246745a513d3d";
|
||||
byte[] bytes = TypeUtil.fromHexString(encoded);
|
||||
byte[] bytes = StringUtil.fromHexString(encoded);
|
||||
byte[] array = new byte[bytes.length + 1];
|
||||
System.arraycopy(bytes, 0, array, 1, bytes.length);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(array, 1, bytes.length).slice();
|
||||
|
@ -162,10 +166,10 @@ public class HpackDecoderTest
|
|||
@Test
|
||||
public void testDecodeHuffmanWithArrayOffset() throws Exception
|
||||
{
|
||||
HpackDecoder decoder = new HpackDecoder(4096, 8192);
|
||||
HpackDecoder decoder = new HpackDecoder(8192);
|
||||
|
||||
String encoded = "8286418cf1e3c2e5f23a6ba0ab90f4ff84";
|
||||
byte[] bytes = TypeUtil.fromHexString(encoded);
|
||||
byte[] bytes = StringUtil.fromHexString(encoded);
|
||||
byte[] array = new byte[bytes.length + 1];
|
||||
System.arraycopy(bytes, 0, array, 1, bytes.length);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(array, 1, bytes.length).slice();
|
||||
|
@ -184,9 +188,9 @@ public class HpackDecoderTest
|
|||
{
|
||||
// Response encoded by nghttpx
|
||||
String encoded = "886196C361Be940b6a65B6850400B8A00571972e080a62D1Bf5f87497cA589D34d1f9a0f0d0234327690Aa69D29aFcA954D3A5358980Ae112e0f7c880aE152A9A74a6bF3";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
|
||||
|
||||
HpackDecoder decoder = new HpackDecoder(4096, 8192);
|
||||
HpackDecoder decoder = new HpackDecoder(8192);
|
||||
MetaData.Response response = (MetaData.Response)decoder.decode(buffer);
|
||||
|
||||
assertThat(response.getStatus(), is(200));
|
||||
|
@ -203,8 +207,8 @@ public class HpackDecoderTest
|
|||
public void testResize() throws Exception
|
||||
{
|
||||
String encoded = "203f136687A0E41d139d090760881c6490B2Cd39Ba7f";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
HpackDecoder decoder = new HpackDecoder(4096, 8192);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
|
||||
HpackDecoder decoder = new HpackDecoder(8192);
|
||||
MetaData metaData = decoder.decode(buffer);
|
||||
assertThat(metaData.getFields().get(HttpHeader.HOST), is("localhost0"));
|
||||
assertThat(metaData.getFields().get(HttpHeader.COOKIE), is("abcdefghij"));
|
||||
|
@ -225,8 +229,8 @@ public class HpackDecoderTest
|
|||
*/
|
||||
|
||||
String encoded = "203f136687A0E41d139d090760881c6490B2Cd39Ba7f20";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
HpackDecoder decoder = new HpackDecoder(4096, 8192);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
|
||||
HpackDecoder decoder = new HpackDecoder(8192);
|
||||
try
|
||||
{
|
||||
decoder.decode(buffer);
|
||||
|
@ -242,9 +246,10 @@ public class HpackDecoderTest
|
|||
public void testTooBigToIndex() throws Exception
|
||||
{
|
||||
String encoded = "3f610f17FfEc02Df3990A190A0D4Ee5b3d2940Ec98Aa4a62D127D29e273a0aA20dEcAa190a503b262d8a2671D4A2672a927aA874988a2471D05510750c951139EdA2452a3a548cAa1aA90bE4B228342864A9E0D450A5474a92992a1aA513395448E3A0Aa17B96cFe3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f14E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F353F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F54f";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
|
||||
|
||||
HpackDecoder decoder = new HpackDecoder(128, 8192);
|
||||
HpackDecoder decoder = new HpackDecoder(8192);
|
||||
decoder.setMaxTableCapacity(128);
|
||||
MetaData metaData = decoder.decode(buffer);
|
||||
|
||||
assertThat(decoder.getHpackContext().getDynamicTableSize(), is(0));
|
||||
|
@ -255,9 +260,10 @@ public class HpackDecoderTest
|
|||
public void testUnknownIndex() throws Exception
|
||||
{
|
||||
String encoded = "BE";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
|
||||
|
||||
HpackDecoder decoder = new HpackDecoder(128, 8192);
|
||||
HpackDecoder decoder = new HpackDecoder(8192);
|
||||
decoder.setMaxTableCapacity(128);
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -442,10 +448,10 @@ public class HpackDecoderTest
|
|||
@Test
|
||||
public void testHuffmanEncodedStandard() throws Exception
|
||||
{
|
||||
HpackDecoder decoder = new HpackDecoder(4096, 8192);
|
||||
HpackDecoder decoder = new HpackDecoder(8192);
|
||||
|
||||
String encoded = "82868441" + "83" + "49509F";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
|
||||
|
||||
MetaData.Request request = (MetaData.Request)decoder.decode(buffer);
|
||||
|
||||
|
@ -460,10 +466,10 @@ public class HpackDecoderTest
|
|||
@Test
|
||||
public void testHuffmanEncodedExtraPadding()
|
||||
{
|
||||
HpackDecoder decoder = new HpackDecoder(4096, 8192);
|
||||
HpackDecoder decoder = new HpackDecoder(8192);
|
||||
|
||||
String encoded = "82868441" + "84" + "49509FFF";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
|
||||
CompressionException ex = assertThrows(CompressionException.class, () -> decoder.decode(buffer));
|
||||
assertThat(ex.getMessage(), Matchers.containsString("bad_termination"));
|
||||
}
|
||||
|
@ -472,10 +478,10 @@ public class HpackDecoderTest
|
|||
@Test
|
||||
public void testHuffmanEncodedZeroPadding()
|
||||
{
|
||||
HpackDecoder decoder = new HpackDecoder(4096, 8192);
|
||||
HpackDecoder decoder = new HpackDecoder(8192);
|
||||
|
||||
String encoded = "82868441" + "83" + "495090";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
|
||||
|
||||
CompressionException ex = assertThrows(CompressionException.class, () -> decoder.decode(buffer));
|
||||
assertThat(ex.getMessage(), Matchers.containsString("incorrect_padding"));
|
||||
|
@ -485,10 +491,10 @@ public class HpackDecoderTest
|
|||
@Test
|
||||
public void testHuffmanEncodedWithEOS()
|
||||
{
|
||||
HpackDecoder decoder = new HpackDecoder(4096, 8192);
|
||||
HpackDecoder decoder = new HpackDecoder(8192);
|
||||
|
||||
String encoded = "82868441" + "87" + "497FFFFFFF427F";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
|
||||
|
||||
CompressionException ex = assertThrows(CompressionException.class, () -> decoder.decode(buffer));
|
||||
assertThat(ex.getMessage(), Matchers.containsString("eos_in_content"));
|
||||
|
@ -497,10 +503,10 @@ public class HpackDecoderTest
|
|||
@Test
|
||||
public void testHuffmanEncodedOneIncompleteOctet()
|
||||
{
|
||||
HpackDecoder decoder = new HpackDecoder(4096, 8192);
|
||||
HpackDecoder decoder = new HpackDecoder(8192);
|
||||
|
||||
String encoded = "82868441" + "81" + "FE";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
|
||||
|
||||
CompressionException ex = assertThrows(CompressionException.class, () -> decoder.decode(buffer));
|
||||
assertThat(ex.getMessage(), Matchers.containsString("bad_termination"));
|
||||
|
@ -509,10 +515,10 @@ public class HpackDecoderTest
|
|||
@Test
|
||||
public void testHuffmanEncodedTwoIncompleteOctet()
|
||||
{
|
||||
HpackDecoder decoder = new HpackDecoder(4096, 8192);
|
||||
HpackDecoder decoder = new HpackDecoder(8192);
|
||||
|
||||
String encoded = "82868441" + "82" + "FFFE";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
|
||||
|
||||
CompressionException ex = assertThrows(CompressionException.class, () -> decoder.decode(buffer));
|
||||
assertThat(ex.getMessage(), Matchers.containsString("bad_termination"));
|
||||
|
@ -521,10 +527,10 @@ public class HpackDecoderTest
|
|||
@Test
|
||||
public void testZeroLengthName()
|
||||
{
|
||||
HpackDecoder decoder = new HpackDecoder(4096, 8192);
|
||||
HpackDecoder decoder = new HpackDecoder(8192);
|
||||
|
||||
String encoded = "00000130";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
|
||||
SessionException ex = assertThrows(SessionException.class, () -> decoder.decode(buffer));
|
||||
assertThat(ex.getMessage(), Matchers.containsString("Header size 0"));
|
||||
}
|
||||
|
@ -532,10 +538,10 @@ public class HpackDecoderTest
|
|||
@Test
|
||||
public void testZeroLengthValue() throws Exception
|
||||
{
|
||||
HpackDecoder decoder = new HpackDecoder(4096, 8192);
|
||||
HpackDecoder decoder = new HpackDecoder(8192);
|
||||
|
||||
String encoded = "00016800";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
|
||||
MetaData metaData = decoder.decode(buffer);
|
||||
assertThat(metaData.getFields().size(), is(1));
|
||||
assertThat(metaData.getFields().get("h"), is(""));
|
||||
|
@ -544,10 +550,10 @@ public class HpackDecoderTest
|
|||
@Test
|
||||
public void testUpperCaseName()
|
||||
{
|
||||
HpackDecoder decoder = new HpackDecoder(4096, 8192);
|
||||
HpackDecoder decoder = new HpackDecoder(8192);
|
||||
|
||||
String encoded = "0001480130";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
|
||||
StreamException ex = assertThrows(StreamException.class, () -> decoder.decode(buffer));
|
||||
assertThat(ex.getMessage(), Matchers.containsString("Uppercase header"));
|
||||
}
|
||||
|
@ -555,10 +561,10 @@ public class HpackDecoderTest
|
|||
@Test
|
||||
public void testWhiteSpaceName()
|
||||
{
|
||||
HpackDecoder decoder = new HpackDecoder(4096, 8192);
|
||||
HpackDecoder decoder = new HpackDecoder(8192);
|
||||
|
||||
String encoded = "0001200130";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
|
||||
StreamException ex = assertThrows(StreamException.class, () -> decoder.decode(buffer));
|
||||
assertThat(ex.getMessage(), Matchers.containsString("Illegal header"));
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ public class HpackEncoderTest
|
|||
@Test
|
||||
public void testUnknownFieldsContextManagement() throws Exception
|
||||
{
|
||||
HpackEncoder encoder = new HpackEncoder(38 * 5);
|
||||
HpackEncoder encoder = newHpackEncoder(38 * 5);
|
||||
HttpFields.Mutable fields = HttpFields.build();
|
||||
|
||||
HttpField[] field =
|
||||
|
@ -144,8 +144,9 @@ public class HpackEncoderTest
|
|||
@Test
|
||||
public void testLargeFieldsNotIndexed()
|
||||
{
|
||||
HpackEncoder encoder = new HpackEncoder(38 * 5);
|
||||
HpackEncoder encoder = newHpackEncoder(38 * 5);
|
||||
HpackContext ctx = encoder.getHpackContext();
|
||||
ctx.resize(encoder.getMaxTableCapacity());
|
||||
|
||||
ByteBuffer buffer = BufferUtil.allocate(4096);
|
||||
|
||||
|
@ -170,8 +171,9 @@ public class HpackEncoderTest
|
|||
@Test
|
||||
public void testIndexContentLength()
|
||||
{
|
||||
HpackEncoder encoder = new HpackEncoder(38 * 5);
|
||||
HpackEncoder encoder = newHpackEncoder(38 * 5);
|
||||
HpackContext ctx = encoder.getHpackContext();
|
||||
ctx.resize(encoder.getMaxTableCapacity());
|
||||
|
||||
ByteBuffer buffer = BufferUtil.allocate(4096);
|
||||
|
||||
|
@ -192,7 +194,7 @@ public class HpackEncoderTest
|
|||
@Test
|
||||
public void testNeverIndexSetCookie() throws Exception
|
||||
{
|
||||
HpackEncoder encoder = new HpackEncoder(38 * 5);
|
||||
HpackEncoder encoder = newHpackEncoder(38 * 5);
|
||||
ByteBuffer buffer = BufferUtil.allocate(4096);
|
||||
|
||||
HttpFields.Mutable fields = HttpFields.build()
|
||||
|
@ -226,20 +228,20 @@ public class HpackEncoderTest
|
|||
{
|
||||
HttpFields.Mutable fields = HttpFields.build();
|
||||
|
||||
HpackEncoder encoder = new HpackEncoder(128);
|
||||
HpackEncoder encoder = newHpackEncoder(128);
|
||||
ByteBuffer buffer0 = BufferUtil.allocate(4096);
|
||||
int pos = BufferUtil.flipToFill(buffer0);
|
||||
encoder.encode(buffer0, new MetaData(HttpVersion.HTTP_2, fields));
|
||||
BufferUtil.flipToFlush(buffer0, pos);
|
||||
|
||||
encoder = new HpackEncoder(128);
|
||||
encoder = newHpackEncoder(128);
|
||||
fields.add(new HttpField("user-agent", "jetty/test"));
|
||||
ByteBuffer buffer1 = BufferUtil.allocate(4096);
|
||||
pos = BufferUtil.flipToFill(buffer1);
|
||||
encoder.encode(buffer1, new MetaData(HttpVersion.HTTP_2, fields));
|
||||
BufferUtil.flipToFlush(buffer1, pos);
|
||||
|
||||
encoder = new HpackEncoder(128);
|
||||
encoder = newHpackEncoder(128);
|
||||
encoder.setValidateEncoding(false);
|
||||
fields.add(new HttpField(":path",
|
||||
"This is a very large field, whose size is larger than the dynamic table so it should not be indexed as it will not fit in the table ever!" +
|
||||
|
@ -251,7 +253,7 @@ public class HpackEncoderTest
|
|||
encoder.encode(buffer2, new MetaData(HttpVersion.HTTP_2, fields));
|
||||
BufferUtil.flipToFlush(buffer2, pos);
|
||||
|
||||
encoder = new HpackEncoder(128);
|
||||
encoder = newHpackEncoder(128);
|
||||
encoder.setValidateEncoding(false);
|
||||
fields.add(new HttpField("host", "somehost"));
|
||||
ByteBuffer buffer = BufferUtil.allocate(4096);
|
||||
|
@ -292,12 +294,12 @@ public class HpackEncoderTest
|
|||
.add("host", "localhost0")
|
||||
.add("cookie", "abcdefghij");
|
||||
|
||||
HpackEncoder encoder = new HpackEncoder(4096);
|
||||
HpackEncoder encoder = newHpackEncoder(4096);
|
||||
|
||||
ByteBuffer buffer = BufferUtil.allocate(4096);
|
||||
int pos = BufferUtil.flipToFill(buffer);
|
||||
encoder.encodeMaxDynamicTableSize(buffer, 0);
|
||||
encoder.setRemoteMaxDynamicTableSize(50);
|
||||
encoder.setTableCapacity(50);
|
||||
encoder.encode(buffer, new MetaData(HttpVersion.HTTP_2, fields));
|
||||
BufferUtil.flipToFlush(buffer, pos);
|
||||
|
||||
|
@ -306,4 +308,12 @@ public class HpackEncoderTest
|
|||
assertThat(context.getMaxDynamicTableSize(), Matchers.is(50));
|
||||
assertThat(context.size(), Matchers.is(1));
|
||||
}
|
||||
|
||||
private static HpackEncoder newHpackEncoder(int tableCapacity)
|
||||
{
|
||||
HpackEncoder encoder = new HpackEncoder();
|
||||
encoder.setMaxTableCapacity(tableCapacity);
|
||||
encoder.setTableCapacity(tableCapacity);
|
||||
return encoder;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|||
|
||||
public class HpackPerfTest
|
||||
{
|
||||
int _maxDynamicTableSize = 4 * 1024;
|
||||
int _tableCapacity = 4 * 1024;
|
||||
int _unencodedSize;
|
||||
int _encodedSize;
|
||||
|
||||
|
@ -46,7 +46,7 @@ public class HpackPerfTest
|
|||
@AfterEach
|
||||
public void after()
|
||||
{
|
||||
System.err.printf("dynamictable=%d unencoded=%d encoded=%d p=%3.1f%%%n", _maxDynamicTableSize, _unencodedSize, _encodedSize, 100.0 * _encodedSize / _unencodedSize);
|
||||
System.err.printf("dynamictable=%d unencoded=%d encoded=%d p=%3.1f%%%n", _tableCapacity, _unencodedSize, _encodedSize, 100.0 * _encodedSize / _unencodedSize);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -92,7 +92,9 @@ public class HpackPerfTest
|
|||
{
|
||||
if (type.equals(story.get("context")))
|
||||
{
|
||||
HpackEncoder encoder = new HpackEncoder(_maxDynamicTableSize, _maxDynamicTableSize);
|
||||
HpackEncoder encoder = new HpackEncoder();
|
||||
encoder.setMaxTableCapacity(_tableCapacity);
|
||||
encoder.setTableCapacity(_tableCapacity);
|
||||
encoder.setValidateEncoding(false);
|
||||
|
||||
Object[] cases = (Object[])story.get("cases");
|
||||
|
|
|
@ -43,7 +43,7 @@ public class HpackTest
|
|||
public void encodeDecodeResponseTest() throws Exception
|
||||
{
|
||||
HpackEncoder encoder = new HpackEncoder();
|
||||
HpackDecoder decoder = new HpackDecoder(4096, 8192);
|
||||
HpackDecoder decoder = new HpackDecoder(8192);
|
||||
ByteBuffer buffer = BufferUtil.allocateDirect(16 * 1024);
|
||||
|
||||
HttpFields.Mutable fields0 = HttpFields.build()
|
||||
|
@ -98,7 +98,7 @@ public class HpackTest
|
|||
public void encodeDecodeTooLargeTest() throws Exception
|
||||
{
|
||||
HpackEncoder encoder = new HpackEncoder();
|
||||
HpackDecoder decoder = new HpackDecoder(4096, 164);
|
||||
HpackDecoder decoder = new HpackDecoder(164);
|
||||
ByteBuffer buffer = BufferUtil.allocateDirect(16 * 1024);
|
||||
|
||||
HttpFields fields0 = HttpFields.build()
|
||||
|
@ -158,8 +158,11 @@ public class HpackTest
|
|||
@Test
|
||||
public void evictReferencedFieldTest() throws Exception
|
||||
{
|
||||
HpackEncoder encoder = new HpackEncoder(200, 200);
|
||||
HpackDecoder decoder = new HpackDecoder(200, 1024);
|
||||
HpackDecoder decoder = new HpackDecoder(1024);
|
||||
decoder.setMaxTableCapacity(200);
|
||||
HpackEncoder encoder = new HpackEncoder();
|
||||
encoder.setMaxTableCapacity(decoder.getMaxTableCapacity());
|
||||
encoder.setTableCapacity(decoder.getMaxTableCapacity());
|
||||
ByteBuffer buffer = BufferUtil.allocateDirect(16 * 1024);
|
||||
|
||||
String longEnoughToBeEvicted = "012345678901234567890123456789012345678901234567890";
|
||||
|
@ -202,7 +205,7 @@ public class HpackTest
|
|||
public void testHopHeadersAreRemoved() throws Exception
|
||||
{
|
||||
HpackEncoder encoder = new HpackEncoder();
|
||||
HpackDecoder decoder = new HpackDecoder(4096, 16384);
|
||||
HpackDecoder decoder = new HpackDecoder(16384);
|
||||
|
||||
HttpFields input = HttpFields.build()
|
||||
.add(HttpHeader.ACCEPT, "*")
|
||||
|
@ -229,7 +232,7 @@ public class HpackTest
|
|||
public void testTETrailers() throws Exception
|
||||
{
|
||||
HpackEncoder encoder = new HpackEncoder();
|
||||
HpackDecoder decoder = new HpackDecoder(4096, 16384);
|
||||
HpackDecoder decoder = new HpackDecoder(16384);
|
||||
|
||||
String teValue = "trailers";
|
||||
String trailerValue = "Custom";
|
||||
|
@ -254,7 +257,7 @@ public class HpackTest
|
|||
public void testColonHeaders() throws Exception
|
||||
{
|
||||
HpackEncoder encoder = new HpackEncoder();
|
||||
HpackDecoder decoder = new HpackDecoder(4096, 16384);
|
||||
HpackDecoder decoder = new HpackDecoder(16384);
|
||||
|
||||
HttpFields input = HttpFields.build()
|
||||
.add(":status", "200")
|
||||
|
|
|
@ -31,7 +31,6 @@ import java.util.concurrent.TimeUnit;
|
|||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.UnaryOperator;
|
||||
import java.util.stream.IntStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
@ -460,7 +459,8 @@ public class HttpClientTransportOverHTTP2Test extends AbstractTest
|
|||
OutputStream output = socket.getOutputStream();
|
||||
InputStream input = socket.getInputStream();
|
||||
|
||||
ServerParser parser = new ServerParser(byteBufferPool, new ServerParser.Listener.Adapter()
|
||||
ServerParser parser = new ServerParser(byteBufferPool, 8192, RateControl.NO_RATE_CONTROL);
|
||||
parser.init(new ServerParser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onPreface()
|
||||
|
@ -512,8 +512,7 @@ public class HttpClientTransportOverHTTP2Test extends AbstractTest
|
|||
x.printStackTrace();
|
||||
}
|
||||
}
|
||||
}, 4096, 8192, RateControl.NO_RATE_CONTROL);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
byte[] bytes = new byte[1024];
|
||||
while (true)
|
||||
|
@ -586,7 +585,8 @@ public class HttpClientTransportOverHTTP2Test extends AbstractTest
|
|||
public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
|
||||
{
|
||||
// Disable checks for invalid headers.
|
||||
((HTTP2Session)stream.getSession()).getGenerator().setValidateHpackEncoding(false);
|
||||
Generator generator = ((HTTP2Session)stream.getSession()).getGenerator();
|
||||
generator.getHpackEncoder().setValidateEncoding(false);
|
||||
// Produce an invalid HPACK block by adding a request pseudo-header to the response.
|
||||
HttpFields fields = HttpFields.build()
|
||||
.put(":method", "get");
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.eclipse.jetty.http2.api.server.ServerSessionListener;
|
|||
import org.eclipse.jetty.http2.frames.Frame;
|
||||
import org.eclipse.jetty.http2.frames.SettingsFrame;
|
||||
import org.eclipse.jetty.http2.generator.Generator;
|
||||
import org.eclipse.jetty.http2.hpack.HpackContext;
|
||||
import org.eclipse.jetty.http2.parser.RateControl;
|
||||
import org.eclipse.jetty.http2.parser.ServerParser;
|
||||
import org.eclipse.jetty.http2.parser.WindowRateControl;
|
||||
|
@ -53,12 +54,13 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne
|
|||
{
|
||||
private final HTTP2SessionContainer sessionContainer = new HTTP2SessionContainer();
|
||||
private final HttpConfiguration httpConfiguration;
|
||||
private int maxDynamicTableSize = 4096;
|
||||
private int maxDecoderTableCapacity = HpackContext.DEFAULT_MAX_TABLE_CAPACITY;
|
||||
private int maxEncoderTableCapacity = HpackContext.DEFAULT_MAX_TABLE_CAPACITY;
|
||||
private int initialSessionRecvWindow = 1024 * 1024;
|
||||
private int initialStreamRecvWindow = 512 * 1024;
|
||||
private int maxConcurrentStreams = 128;
|
||||
private int maxHeaderBlockFragment = 0;
|
||||
private int maxFrameLength = Frame.DEFAULT_MAX_LENGTH;
|
||||
private int maxFrameSize = Frame.DEFAULT_MAX_LENGTH;
|
||||
private int maxSettingsKeys = SettingsFrame.DEFAULT_MAX_KEYS;
|
||||
private boolean connectProtocolEnabled = true;
|
||||
private RateControl.Factory rateControlFactory = new WindowRateControl.Factory(50);
|
||||
|
@ -88,15 +90,52 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne
|
|||
setUseOutputDirectByteBuffers(httpConfiguration.isUseOutputDirectByteBuffers());
|
||||
}
|
||||
|
||||
@ManagedAttribute("The HPACK dynamic table maximum size")
|
||||
public int getMaxDynamicTableSize()
|
||||
@ManagedAttribute("The HPACK encoder dynamic table maximum capacity")
|
||||
public int getMaxEncoderTableCapacity()
|
||||
{
|
||||
return maxDynamicTableSize;
|
||||
return maxEncoderTableCapacity;
|
||||
}
|
||||
|
||||
public void setMaxDynamicTableSize(int maxDynamicTableSize)
|
||||
/**
|
||||
* <p>Sets the limit for the encoder HPACK dynamic table capacity.</p>
|
||||
* <p>Setting this value to {@code 0} disables the use of the dynamic table.</p>
|
||||
*
|
||||
* @param maxEncoderTableCapacity The HPACK encoder dynamic table maximum capacity
|
||||
*/
|
||||
public void setMaxEncoderTableCapacity(int maxEncoderTableCapacity)
|
||||
{
|
||||
this.maxDynamicTableSize = maxDynamicTableSize;
|
||||
this.maxEncoderTableCapacity = maxEncoderTableCapacity;
|
||||
}
|
||||
|
||||
@ManagedAttribute("The HPACK decoder dynamic table maximum capacity")
|
||||
public int getMaxDecoderTableCapacity()
|
||||
{
|
||||
return maxDecoderTableCapacity;
|
||||
}
|
||||
|
||||
public void setMaxDecoderTableCapacity(int maxDecoderTableCapacity)
|
||||
{
|
||||
this.maxDecoderTableCapacity = maxDecoderTableCapacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the max decoder table size
|
||||
* @deprecated use {@link #getMaxDecoderTableCapacity()} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public int getMaxDynamicTableSize()
|
||||
{
|
||||
return getMaxDecoderTableCapacity();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param maxTableSize the max decoder table size
|
||||
* @deprecated use {@link #setMaxDecoderTableCapacity(int)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public void setMaxDynamicTableSize(int maxTableSize)
|
||||
{
|
||||
setMaxDecoderTableCapacity(maxTableSize);
|
||||
}
|
||||
|
||||
@ManagedAttribute("The initial size of session's flow control receive window")
|
||||
|
@ -164,15 +203,27 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne
|
|||
this.streamIdleTimeout = streamIdleTimeout;
|
||||
}
|
||||
|
||||
@ManagedAttribute("The max frame length in bytes")
|
||||
@Deprecated
|
||||
public int getMaxFrameLength()
|
||||
{
|
||||
return maxFrameLength;
|
||||
return getMaxFrameSize();
|
||||
}
|
||||
|
||||
public void setMaxFrameLength(int maxFrameLength)
|
||||
@Deprecated
|
||||
public void setMaxFrameLength(int maxFrameSize)
|
||||
{
|
||||
this.maxFrameLength = maxFrameLength;
|
||||
setMaxFrameSize(maxFrameSize);
|
||||
}
|
||||
|
||||
@ManagedAttribute("The max frame size in bytes")
|
||||
public int getMaxFrameSize()
|
||||
{
|
||||
return maxFrameSize;
|
||||
}
|
||||
|
||||
public void setMaxFrameSize(int maxFrameSize)
|
||||
{
|
||||
this.maxFrameSize = maxFrameSize;
|
||||
}
|
||||
|
||||
@ManagedAttribute("The max number of keys in all SETTINGS frames")
|
||||
|
@ -245,12 +296,16 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne
|
|||
protected Map<Integer, Integer> newSettings()
|
||||
{
|
||||
Map<Integer, Integer> settings = new HashMap<>();
|
||||
settings.put(SettingsFrame.HEADER_TABLE_SIZE, getMaxDynamicTableSize());
|
||||
settings.put(SettingsFrame.INITIAL_WINDOW_SIZE, getInitialStreamRecvWindow());
|
||||
int maxConcurrentStreams = getMaxConcurrentStreams();
|
||||
if (maxConcurrentStreams >= 0)
|
||||
settings.put(SettingsFrame.MAX_CONCURRENT_STREAMS, maxConcurrentStreams);
|
||||
settings.put(SettingsFrame.MAX_HEADER_LIST_SIZE, getHttpConfiguration().getRequestHeaderSize());
|
||||
int maxTableSize = getMaxDecoderTableCapacity();
|
||||
if (maxTableSize != HpackContext.DEFAULT_MAX_TABLE_CAPACITY)
|
||||
settings.put(SettingsFrame.HEADER_TABLE_SIZE, maxTableSize);
|
||||
int initialStreamRecvWindow = getInitialStreamRecvWindow();
|
||||
if (initialStreamRecvWindow != FlowControlStrategy.DEFAULT_WINDOW_SIZE)
|
||||
settings.put(SettingsFrame.INITIAL_WINDOW_SIZE, initialStreamRecvWindow);
|
||||
settings.put(SettingsFrame.MAX_CONCURRENT_STREAMS, getMaxConcurrentStreams());
|
||||
int maxHeadersSize = getHttpConfiguration().getRequestHeaderSize();
|
||||
if (maxHeadersSize > 0)
|
||||
settings.put(SettingsFrame.MAX_HEADER_LIST_SIZE, maxHeadersSize);
|
||||
settings.put(SettingsFrame.ENABLE_CONNECT_PROTOCOL, isConnectProtocolEnabled() ? 1 : 0);
|
||||
return settings;
|
||||
}
|
||||
|
@ -260,11 +315,17 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne
|
|||
{
|
||||
ServerSessionListener listener = newSessionListener(connector, endPoint);
|
||||
|
||||
Generator generator = new Generator(connector.getByteBufferPool(), isUseOutputDirectByteBuffers(), getMaxDynamicTableSize(), getMaxHeaderBlockFragment());
|
||||
Generator generator = new Generator(connector.getByteBufferPool(), isUseOutputDirectByteBuffers(), getMaxHeaderBlockFragment());
|
||||
FlowControlStrategy flowControl = getFlowControlStrategyFactory().newFlowControlStrategy();
|
||||
HTTP2ServerSession session = new HTTP2ServerSession(connector.getScheduler(), endPoint, generator, listener, flowControl);
|
||||
|
||||
ServerParser parser = newServerParser(connector, getRateControlFactory().newRateControl(endPoint));
|
||||
parser.setMaxFrameSize(getMaxFrameSize());
|
||||
parser.setMaxSettingsKeys(getMaxSettingsKeys());
|
||||
|
||||
HTTP2ServerSession session = new HTTP2ServerSession(connector.getScheduler(), endPoint, parser, generator, listener, flowControl);
|
||||
session.setMaxLocalStreams(getMaxConcurrentStreams());
|
||||
session.setMaxRemoteStreams(getMaxConcurrentStreams());
|
||||
session.setMaxEncoderTableCapacity(getMaxEncoderTableCapacity());
|
||||
// For a single stream in a connection, there will be a race between
|
||||
// the stream idle timeout and the connection idle timeout. However,
|
||||
// the typical case is that the connection will be busier and the
|
||||
|
@ -276,25 +337,22 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne
|
|||
session.setWriteThreshold(getHttpConfiguration().getOutputBufferSize());
|
||||
session.setConnectProtocolEnabled(isConnectProtocolEnabled());
|
||||
|
||||
ServerParser parser = newServerParser(connector, session, getRateControlFactory().newRateControl(endPoint));
|
||||
parser.setMaxFrameLength(getMaxFrameLength());
|
||||
parser.setMaxSettingsKeys(getMaxSettingsKeys());
|
||||
|
||||
RetainableByteBufferPool retainableByteBufferPool = connector.getByteBufferPool().asRetainableByteBufferPool();
|
||||
|
||||
HTTP2Connection connection = new HTTP2ServerConnection(retainableByteBufferPool, connector.getExecutor(),
|
||||
endPoint, httpConfiguration, parser, session, getInputBufferSize(), listener);
|
||||
endPoint, httpConfiguration, session, getInputBufferSize(), listener);
|
||||
connection.setUseInputDirectByteBuffers(isUseInputDirectByteBuffers());
|
||||
connection.setUseOutputDirectByteBuffers(isUseOutputDirectByteBuffers());
|
||||
connection.addEventListener(sessionContainer);
|
||||
parser.init(connection);
|
||||
|
||||
return configure(connection, connector, endPoint);
|
||||
}
|
||||
|
||||
protected abstract ServerSessionListener newSessionListener(Connector connector, EndPoint endPoint);
|
||||
|
||||
protected ServerParser newServerParser(Connector connector, ServerParser.Listener listener, RateControl rateControl)
|
||||
protected ServerParser newServerParser(Connector connector, RateControl rateControl)
|
||||
{
|
||||
return new ServerParser(connector.getByteBufferPool(), listener, getMaxDynamicTableSize(), getHttpConfiguration().getRequestHeaderSize(), rateControl);
|
||||
return new ServerParser(connector.getByteBufferPool(), getHttpConfiguration().getRequestHeaderSize(), rateControl);
|
||||
}
|
||||
|
||||
@ManagedObject("The container of HTTP/2 sessions")
|
||||
|
|
|
@ -53,7 +53,7 @@ import org.eclipse.jetty.util.Callback;
|
|||
import org.eclipse.jetty.util.TypeUtil;
|
||||
import org.eclipse.jetty.util.thread.AutoLock;
|
||||
|
||||
public class HTTP2ServerConnection extends HTTP2Connection
|
||||
public class HTTP2ServerConnection extends HTTP2Connection implements ServerParser.Listener
|
||||
{
|
||||
/**
|
||||
* @param protocol An HTTP2 protocol variant
|
||||
|
@ -86,19 +86,13 @@ public class HTTP2ServerConnection extends HTTP2Connection
|
|||
private final HttpConfiguration httpConfig;
|
||||
private boolean recycleHttpChannels = true;
|
||||
|
||||
public HTTP2ServerConnection(RetainableByteBufferPool retainableByteBufferPool, Executor executor, EndPoint endPoint, HttpConfiguration httpConfig, ServerParser parser, ISession session, int inputBufferSize, ServerSessionListener listener)
|
||||
public HTTP2ServerConnection(RetainableByteBufferPool retainableByteBufferPool, Executor executor, EndPoint endPoint, HttpConfiguration httpConfig, HTTP2ServerSession session, int inputBufferSize, ServerSessionListener listener)
|
||||
{
|
||||
super(retainableByteBufferPool, executor, endPoint, parser, session, inputBufferSize);
|
||||
super(retainableByteBufferPool, executor, endPoint, session, inputBufferSize);
|
||||
this.listener = listener;
|
||||
this.httpConfig = httpConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ServerParser getParser()
|
||||
{
|
||||
return (ServerParser)super.getParser();
|
||||
}
|
||||
|
||||
public boolean isRecycleHttpChannels()
|
||||
{
|
||||
return recycleHttpChannels;
|
||||
|
@ -134,6 +128,12 @@ public class HTTP2ServerConnection extends HTTP2Connection
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPreface()
|
||||
{
|
||||
((HTTP2ServerSession)getSession()).onPreface();
|
||||
}
|
||||
|
||||
public void onNewStream(Connector connector, IStream stream, HeadersFrame frame)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
|
@ -215,7 +215,7 @@ public class HTTP2ServerConnection extends HTTP2Connection
|
|||
.map(HTTP2Channel.Server::isIdle)
|
||||
.reduce(true, Boolean::logicalAnd);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} idle timeout on {}: {}", result ? "Processed" : "Ignored", session, failure);
|
||||
LOG.debug("{} idle timeout on {}", result ? "Processed" : "Ignored", session, failure);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -294,7 +294,7 @@ public class HTTP2ServerConnection extends HTTP2Connection
|
|||
{
|
||||
if (HttpMethod.PRI.is(request.getMethod()))
|
||||
{
|
||||
getParser().directUpgrade();
|
||||
((HTTP2ServerSession)getSession()).directUpgrade();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -317,7 +317,7 @@ public class HTTP2ServerConnection extends HTTP2Connection
|
|||
responseFields.put(HttpHeader.UPGRADE, "h2c");
|
||||
responseFields.put(HttpHeader.CONNECTION, "Upgrade");
|
||||
|
||||
getParser().standardUpgrade();
|
||||
((HTTP2ServerSession)getSession()).standardUpgrade();
|
||||
|
||||
// We fake that we received a client preface, so that we can send the
|
||||
// server preface as the first HTTP/2 frame as required by the spec.
|
||||
|
|
|
@ -46,12 +46,18 @@ public class HTTP2ServerSession extends HTTP2Session implements ServerParser.Lis
|
|||
|
||||
private final ServerSessionListener listener;
|
||||
|
||||
public HTTP2ServerSession(Scheduler scheduler, EndPoint endPoint, Generator generator, ServerSessionListener listener, FlowControlStrategy flowControl)
|
||||
public HTTP2ServerSession(Scheduler scheduler, EndPoint endPoint, ServerParser parser, Generator generator, ServerSessionListener listener, FlowControlStrategy flowControl)
|
||||
{
|
||||
super(scheduler, endPoint, generator, listener, flowControl, 2);
|
||||
super(scheduler, endPoint, parser, generator, listener, flowControl, 2);
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerParser getParser()
|
||||
{
|
||||
return (ServerParser)super.getParser();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPreface()
|
||||
{
|
||||
|
@ -186,4 +192,14 @@ public class HTTP2ServerSession extends HTTP2Session implements ServerParser.Lis
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void directUpgrade()
|
||||
{
|
||||
getParser().directUpgrade();
|
||||
}
|
||||
|
||||
public void standardUpgrade()
|
||||
{
|
||||
getParser().standardUpgrade();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ import java.util.HashMap;
|
|||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
|
@ -87,7 +86,8 @@ public class CloseTest extends AbstractServerTest
|
|||
output.write(BufferUtil.toArray(buffer));
|
||||
}
|
||||
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onHeaders(HeadersFrame frame)
|
||||
|
@ -104,8 +104,7 @@ public class CloseTest extends AbstractServerTest
|
|||
throw new RuntimeIOException(x);
|
||||
}
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
parseResponse(client, parser);
|
||||
|
||||
|
@ -152,7 +151,8 @@ public class CloseTest extends AbstractServerTest
|
|||
// Don't close the connection; the server should close.
|
||||
|
||||
final CountDownLatch responseLatch = new CountDownLatch(1);
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onHeaders(HeadersFrame frame)
|
||||
|
@ -161,8 +161,7 @@ public class CloseTest extends AbstractServerTest
|
|||
// HEADERS, the server is able to send us the response.
|
||||
responseLatch.countDown();
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
parseResponse(client, parser);
|
||||
|
||||
|
@ -217,7 +216,8 @@ public class CloseTest extends AbstractServerTest
|
|||
|
||||
final CountDownLatch responseLatch = new CountDownLatch(1);
|
||||
final CountDownLatch closeLatch = new CountDownLatch(1);
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onHeaders(HeadersFrame frame)
|
||||
|
@ -230,8 +230,7 @@ public class CloseTest extends AbstractServerTest
|
|||
{
|
||||
closeLatch.countDown();
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
parseResponse(client, parser);
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ import java.util.concurrent.CountDownLatch;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import org.eclipse.jetty.http.HostPortHttpField;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
|
@ -147,7 +146,8 @@ public class HTTP2CServerTest extends AbstractServerTest
|
|||
final AtomicReference<HeadersFrame> headersRef = new AtomicReference<>();
|
||||
final AtomicReference<DataFrame> dataRef = new AtomicReference<>();
|
||||
final AtomicReference<CountDownLatch> latchRef = new AtomicReference<>(new CountDownLatch(2));
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onHeaders(HeadersFrame frame)
|
||||
|
@ -162,8 +162,7 @@ public class HTTP2CServerTest extends AbstractServerTest
|
|||
dataRef.set(frame);
|
||||
latchRef.get().countDown();
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
parseResponse(client, parser);
|
||||
|
||||
|
@ -241,7 +240,8 @@ public class HTTP2CServerTest extends AbstractServerTest
|
|||
|
||||
final AtomicReference<HeadersFrame> headersRef = new AtomicReference<>();
|
||||
final AtomicReference<DataFrame> dataRef = new AtomicReference<>();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onSettings(SettingsFrame frame)
|
||||
|
@ -262,8 +262,7 @@ public class HTTP2CServerTest extends AbstractServerTest
|
|||
dataRef.set(frame);
|
||||
latch.countDown();
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
parseResponse(client, parser);
|
||||
|
||||
|
|
|
@ -28,7 +28,6 @@ import java.util.concurrent.CountDownLatch;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.UnaryOperator;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
@ -88,15 +87,15 @@ public class HTTP2ServerTest extends AbstractServerTest
|
|||
}
|
||||
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onGoAway(GoAwayFrame frame)
|
||||
{
|
||||
latch.countDown();
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
parseResponse(client, parser);
|
||||
|
||||
|
@ -132,7 +131,8 @@ public class HTTP2ServerTest extends AbstractServerTest
|
|||
}
|
||||
|
||||
final AtomicReference<HeadersFrame> frameRef = new AtomicReference<>();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onSettings(SettingsFrame frame)
|
||||
|
@ -146,8 +146,7 @@ public class HTTP2ServerTest extends AbstractServerTest
|
|||
frameRef.set(frame);
|
||||
latch.countDown();
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
parseResponse(client, parser);
|
||||
|
||||
|
@ -191,7 +190,8 @@ public class HTTP2ServerTest extends AbstractServerTest
|
|||
|
||||
final AtomicReference<HeadersFrame> headersRef = new AtomicReference<>();
|
||||
final AtomicReference<DataFrame> dataRef = new AtomicReference<>();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onSettings(SettingsFrame frame)
|
||||
|
@ -212,8 +212,7 @@ public class HTTP2ServerTest extends AbstractServerTest
|
|||
dataRef.set(frame);
|
||||
latch.countDown();
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
parseResponse(client, parser);
|
||||
|
||||
|
@ -251,7 +250,8 @@ public class HTTP2ServerTest extends AbstractServerTest
|
|||
output.write(BufferUtil.toArray(buffer));
|
||||
}
|
||||
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onGoAway(GoAwayFrame frame)
|
||||
|
@ -259,8 +259,7 @@ public class HTTP2ServerTest extends AbstractServerTest
|
|||
assertEquals(ErrorCode.FRAME_SIZE_ERROR.code, frame.getError());
|
||||
latch.countDown();
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
parseResponse(client, parser);
|
||||
|
||||
|
@ -289,7 +288,8 @@ public class HTTP2ServerTest extends AbstractServerTest
|
|||
output.write(BufferUtil.toArray(buffer));
|
||||
}
|
||||
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onGoAway(GoAwayFrame frame)
|
||||
|
@ -297,8 +297,7 @@ public class HTTP2ServerTest extends AbstractServerTest
|
|||
assertEquals(ErrorCode.PROTOCOL_ERROR.code, frame.getError());
|
||||
latch.countDown();
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
|
||||
parseResponse(client, parser);
|
||||
|
||||
|
@ -366,8 +365,8 @@ public class HTTP2ServerTest extends AbstractServerTest
|
|||
|
||||
// The server will close the connection abruptly since it
|
||||
// cannot write and therefore cannot even send the GO_AWAY.
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter(), 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter());
|
||||
boolean closed = parseResponse(client, parser, 2 * delay);
|
||||
assertTrue(closed);
|
||||
}
|
||||
|
@ -404,8 +403,8 @@ public class HTTP2ServerTest extends AbstractServerTest
|
|||
}
|
||||
output.flush();
|
||||
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter(), 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter());
|
||||
boolean closed = parseResponse(client, parser);
|
||||
|
||||
assertTrue(closed);
|
||||
|
@ -561,7 +560,7 @@ public class HTTP2ServerTest extends AbstractServerTest
|
|||
return null;
|
||||
}
|
||||
});
|
||||
generator = new Generator(byteBufferPool, 4096, 4);
|
||||
generator = new Generator(byteBufferPool, 4);
|
||||
|
||||
ByteBufferPool.Lease lease = frames.call();
|
||||
|
||||
|
@ -577,7 +576,8 @@ public class HTTP2ServerTest extends AbstractServerTest
|
|||
assertTrue(serverLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
final CountDownLatch clientLatch = new CountDownLatch(1);
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
Parser parser = new Parser(byteBufferPool, 8192);
|
||||
parser.init(new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onHeaders(HeadersFrame frame)
|
||||
|
@ -585,8 +585,7 @@ public class HTTP2ServerTest extends AbstractServerTest
|
|||
if (frame.isEndStream())
|
||||
clientLatch.countDown();
|
||||
}
|
||||
}, 4096, 8192);
|
||||
parser.init(UnaryOperator.identity());
|
||||
});
|
||||
boolean closed = parseResponse(client, parser);
|
||||
|
||||
assertTrue(clientLatch.await(5, TimeUnit.SECONDS));
|
||||
|
|
|
@ -173,7 +173,7 @@ public class ClientHTTP3Session extends ClientProtocolSession
|
|||
{
|
||||
int maxTableCapacity = value.intValue();
|
||||
encoder.setMaxTableCapacity(maxTableCapacity);
|
||||
encoder.setTableCapacity(Math.min(maxTableCapacity, configuration.getInitialEncoderTableCapacity()));
|
||||
encoder.setTableCapacity(Math.min(maxTableCapacity, configuration.getMaxEncoderTableCapacity()));
|
||||
}
|
||||
else if (key == SettingsFrame.MAX_FIELD_SECTION_SIZE)
|
||||
{
|
||||
|
|
|
@ -28,8 +28,8 @@ public class HTTP3Configuration
|
|||
private boolean useInputDirectByteBuffers = true;
|
||||
private boolean useOutputDirectByteBuffers = true;
|
||||
private int maxBlockedStreams = 64;
|
||||
private int maxTableCapacity = 64 * 1024;
|
||||
private int initialTableCapacity = 64 * 1024;
|
||||
private int maxDecoderTableCapacity = 64 * 1024;
|
||||
private int maxEncoderTableCapacity = 64 * 1024;
|
||||
private int maxRequestHeadersSize = 8 * 1024;
|
||||
private int maxResponseHeadersSize = 8 * 1024;
|
||||
|
||||
|
@ -122,7 +122,7 @@ public class HTTP3Configuration
|
|||
@ManagedAttribute("The local QPACK max decoder dynamic table capacity")
|
||||
public int getMaxDecoderTableCapacity()
|
||||
{
|
||||
return maxTableCapacity;
|
||||
return maxDecoderTableCapacity;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -132,17 +132,17 @@ public class HTTP3Configuration
|
|||
* communicated to the remote QPACK encoder via the SETTINGS frame.</p>
|
||||
*
|
||||
* @param maxTableCapacity the QPACK decoder dynamic table max capacity
|
||||
* @see #setInitialEncoderTableCapacity(int)
|
||||
* @see #setMaxEncoderTableCapacity(int)
|
||||
*/
|
||||
public void setMaxDecoderTableCapacity(int maxTableCapacity)
|
||||
{
|
||||
this.maxTableCapacity = maxTableCapacity;
|
||||
this.maxDecoderTableCapacity = maxTableCapacity;
|
||||
}
|
||||
|
||||
@ManagedAttribute("The local QPACK initial encoder dynamic table capacity")
|
||||
public int getInitialEncoderTableCapacity()
|
||||
public int getMaxEncoderTableCapacity()
|
||||
{
|
||||
return initialTableCapacity;
|
||||
return maxEncoderTableCapacity;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -151,12 +151,12 @@ public class HTTP3Configuration
|
|||
* <p>This value is configured in the local QPACK encoder, and may be
|
||||
* overwritten by a smaller value received via the SETTINGS frame.</p>
|
||||
*
|
||||
* @param initialTableCapacity the QPACK encoder dynamic table initial capacity
|
||||
* @param maxTableCapacity the QPACK encoder dynamic table initial capacity
|
||||
* @see #setMaxDecoderTableCapacity(int)
|
||||
*/
|
||||
public void setInitialEncoderTableCapacity(int initialTableCapacity)
|
||||
public void setMaxEncoderTableCapacity(int maxTableCapacity)
|
||||
{
|
||||
this.initialTableCapacity = initialTableCapacity;
|
||||
this.maxEncoderTableCapacity = maxTableCapacity;
|
||||
}
|
||||
|
||||
@ManagedAttribute("The max number of QPACK blocked streams")
|
||||
|
|
|
@ -172,7 +172,7 @@ public class ServerHTTP3Session extends ServerProtocolSession
|
|||
{
|
||||
int maxTableCapacity = value.intValue();
|
||||
encoder.setMaxTableCapacity(maxTableCapacity);
|
||||
encoder.setTableCapacity(Math.min(maxTableCapacity, configuration.getInitialEncoderTableCapacity()));
|
||||
encoder.setTableCapacity(Math.min(maxTableCapacity, configuration.getMaxEncoderTableCapacity()));
|
||||
}
|
||||
else if (key == SettingsFrame.MAX_FIELD_SECTION_SIZE)
|
||||
{
|
||||
|
|
|
@ -378,7 +378,7 @@ public class ClientServerTest extends AbstractClientServerTest
|
|||
http3Configuration.setMaxRequestHeadersSize(maxRequestHeadersSize);
|
||||
// Disable the dynamic table, otherwise the large header
|
||||
// is sent as string literal on the encoder stream.
|
||||
http3Configuration.setInitialEncoderTableCapacity(0);
|
||||
http3Configuration.setMaxEncoderTableCapacity(0);
|
||||
Session.Client clientSession = newSession(new Session.Client.Listener() {});
|
||||
|
||||
CountDownLatch requestFailureLatch = new CountDownLatch(1);
|
||||
|
@ -459,7 +459,7 @@ public class ClientServerTest extends AbstractClientServerTest
|
|||
HTTP3Configuration http3Configuration = h3.getHTTP3Configuration();
|
||||
// Disable the dynamic table, otherwise the large header
|
||||
// is sent as string literal on the encoder stream.
|
||||
http3Configuration.setInitialEncoderTableCapacity(0);
|
||||
http3Configuration.setMaxEncoderTableCapacity(0);
|
||||
http3Configuration.setMaxResponseHeadersSize(maxResponseHeadersSize);
|
||||
|
||||
Session.Client clientSession = newSession(new Session.Client.Listener()
|
||||
|
|
Loading…
Reference in New Issue