Skip to content

Commit 4f815f6

Browse files
committed
Merged branch 'jetty-12.0.x' into 'jetty-12.1.x'.
Signed-off-by: Simone Bordet <[email protected]>
2 parents ce1526f + 6aaaa15 commit 4f815f6

File tree

13 files changed

+107
-33
lines changed

13 files changed

+107
-33
lines changed

jetty-core/jetty-http2/jetty-http2-client-transport/src/main/java/org/eclipse/jetty/http2/client/transport/HttpClientTransportOverHTTP2.java

+1
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ static void configure(HttpClient httpClient, HTTP2Client http2Client)
103103
http2Client.setUseOutputDirectByteBuffers(httpClient.isUseOutputDirectByteBuffers());
104104
http2Client.setConnectBlocking(httpClient.isConnectBlocking());
105105
http2Client.setBindAddress(httpClient.getBindAddress());
106+
http2Client.setMaxRequestHeadersSize(httpClient.getRequestBufferSize());
106107
http2Client.setMaxResponseHeadersSize(httpClient.getMaxResponseHeadersSize());
107108
}
108109

jetty-core/jetty-http2/jetty-http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2Client.java

+12
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ public class HTTP2Client extends ContainerLifeCycle implements AutoCloseable
113113
private int maxDecoderTableCapacity = HpackContext.DEFAULT_MAX_TABLE_CAPACITY;
114114
private int maxEncoderTableCapacity = HpackContext.DEFAULT_MAX_TABLE_CAPACITY;
115115
private int maxHeaderBlockFragment = 0;
116+
private int maxRequestHeadersSize = 8 * 1024;
116117
private int maxResponseHeadersSize = 8 * 1024;
117118
private FlowControlStrategy.Factory flowControlStrategyFactory = () -> new BufferingFlowControlStrategy(0.5F);
118119
private long streamIdleTimeout;
@@ -357,6 +358,17 @@ public void setMaxHeaderBlockFragment(int maxHeaderBlockFragment)
357358
this.maxHeaderBlockFragment = maxHeaderBlockFragment;
358359
}
359360

361+
@ManagedAttribute("The max size of request headers")
362+
public int getMaxRequestHeadersSize()
363+
{
364+
return maxRequestHeadersSize;
365+
}
366+
367+
public void setMaxRequestHeadersSize(int maxRequestHeadersSize)
368+
{
369+
this.maxRequestHeadersSize = maxRequestHeadersSize;
370+
}
371+
360372
@ManagedAttribute("The max size of response headers")
361373
public int getMaxResponseHeadersSize()
362374
{

jetty-core/jetty-http2/jetty-http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,11 @@ public Connection newConnection(EndPoint endPoint, Map<String, Object> context)
5454
Promise<Session> sessionPromise = (Promise<Session>)context.get(SESSION_PROMISE_CONTEXT_KEY);
5555

5656
Generator generator = new Generator(bufferPool, client.isUseOutputDirectByteBuffers(), client.getMaxHeaderBlockFragment());
57+
generator.getHpackEncoder().setMaxHeaderListSize(client.getMaxRequestHeadersSize());
58+
5759
FlowControlStrategy flowControl = client.getFlowControlStrategyFactory().newFlowControlStrategy();
5860

5961
Parser parser = new Parser(bufferPool, client.getMaxResponseHeadersSize());
60-
parser.setMaxFrameSize(client.getMaxFrameSize());
6162
parser.setMaxSettingsKeys(client.getMaxSettingsKeys());
6263

6364
HTTP2ClientSession session = new HTTP2ClientSession(client.getScheduler(), endPoint, parser, generator, listener, flowControl);

jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/generator/FrameGenerator.java

+6-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.eclipse.jetty.http.MetaData;
1919
import org.eclipse.jetty.http2.frames.Frame;
2020
import org.eclipse.jetty.http2.frames.FrameType;
21+
import org.eclipse.jetty.http2.hpack.HpackContext;
2122
import org.eclipse.jetty.http2.hpack.HpackEncoder;
2223
import org.eclipse.jetty.http2.hpack.HpackException;
2324
import org.eclipse.jetty.io.RetainableByteBuffer;
@@ -49,9 +50,12 @@ public boolean isUseDirectByteBuffers()
4950
return headerGenerator.isUseDirectByteBuffers();
5051
}
5152

52-
protected RetainableByteBuffer encode(HpackEncoder encoder, MetaData metaData, int maxFrameSize) throws HpackException
53+
protected RetainableByteBuffer encode(HpackEncoder encoder, MetaData metaData) throws HpackException
5354
{
54-
RetainableByteBuffer hpacked = headerGenerator.getByteBufferPool().acquire(maxFrameSize, isUseDirectByteBuffers());
55+
int bufferSize = encoder.getMaxHeaderListSize();
56+
if (bufferSize <= 0)
57+
bufferSize = HpackContext.DEFAULT_MAX_HEADER_LIST_SIZE;
58+
RetainableByteBuffer hpacked = headerGenerator.getByteBufferPool().acquire(bufferSize, isUseDirectByteBuffers());
5559
try
5660
{
5761
ByteBuffer byteBuffer = hpacked.getByteBuffer();

jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/generator/HeadersGenerator.java

+18-17
Original file line numberDiff line numberDiff line change
@@ -56,44 +56,47 @@ public int generateHeaders(RetainableByteBuffer.Mutable accumulator, int streamI
5656
throw new IllegalArgumentException("Invalid stream id: " + streamId);
5757

5858
int flags = Flags.NONE;
59-
6059
if (priority != null)
6160
flags = Flags.PRIORITY;
61+
if (endStream)
62+
flags |= Flags.END_STREAM;
6263

6364
// TODO Look for a way of not allocating a large buffer here.
6465
// Possibly the hpack encoder could be changed to take the accumulator, but that is a lot of changes.
6566
// Alternately, we could ensure the accumulator has maxFrameSize space
6667
// So long as the buffer is not sliced into continuations, it at least should be available to aggregate
6768
// subsequent frames into... but likely only a frame header followed by an accumulated data frame.
6869
// It might also be good to be able to split the table into continuation frames as it is generated?
69-
RetainableByteBuffer hpack = encode(encoder, metaData, getMaxFrameSize());
70+
RetainableByteBuffer hpack = encode(encoder, metaData);
7071
BufferUtil.flipToFlush(hpack.getByteBuffer(), 0);
7172
int hpackLength = hpack.remaining();
7273

74+
int maxHeaderBlock = getMaxFrameSize();
75+
if (maxHeaderBlockFragment > 0)
76+
maxHeaderBlock = Math.min(maxHeaderBlock, maxHeaderBlockFragment);
77+
7378
// Split into CONTINUATION frames if necessary.
74-
if (maxHeaderBlockFragment > 0 && hpackLength > maxHeaderBlockFragment)
79+
if (hpackLength > maxHeaderBlock)
7580
{
7681
int start = accumulator.remaining();
77-
if (endStream)
78-
flags |= Flags.END_STREAM;
7982

80-
int length = maxHeaderBlockFragment + (priority == null ? 0 : PriorityFrame.PRIORITY_LENGTH);
83+
int length = maxHeaderBlock + (priority == null ? 0 : PriorityFrame.PRIORITY_LENGTH);
8184

82-
// generate first fragment with as HEADERS with possible priority
85+
// Generate HEADERS frame with possible PRIORITY frame.
8386
generateHeader(accumulator, FrameType.HEADERS, length, flags, streamId);
8487
generatePriority(accumulator, priority);
85-
accumulator.add(hpack.slice(maxHeaderBlockFragment));
86-
hpack.skip(maxHeaderBlockFragment);
88+
accumulator.add(hpack.slice(maxHeaderBlock));
89+
hpack.skip(maxHeaderBlock);
8790

88-
// generate continuation frames that are not the last
89-
while (hpack.remaining() > maxHeaderBlockFragment)
91+
// Generate CONTINUATION frames that are not the last.
92+
while (hpack.remaining() > maxHeaderBlock)
9093
{
91-
generateHeader(accumulator, FrameType.CONTINUATION, maxHeaderBlockFragment, Flags.NONE, streamId);
92-
accumulator.add(hpack.slice(maxHeaderBlockFragment));
93-
hpack.skip(maxHeaderBlockFragment);
94+
generateHeader(accumulator, FrameType.CONTINUATION, maxHeaderBlock, Flags.NONE, streamId);
95+
accumulator.add(hpack.slice(maxHeaderBlock));
96+
hpack.skip(maxHeaderBlock);
9497
}
9598

96-
// generate the last continuation frame
99+
// Generate the last CONTINUATION frame.
97100
generateHeader(accumulator, FrameType.CONTINUATION, hpack.remaining(), Flags.END_HEADERS, streamId);
98101
accumulator.add(hpack);
99102

@@ -102,8 +105,6 @@ public int generateHeaders(RetainableByteBuffer.Mutable accumulator, int streamI
102105
else
103106
{
104107
flags |= Flags.END_HEADERS;
105-
if (endStream)
106-
flags |= Flags.END_STREAM;
107108

108109
int length = hpackLength + (priority == null ? 0 : PriorityFrame.PRIORITY_LENGTH);
109110
generateHeader(accumulator, FrameType.HEADERS, length, flags, streamId);

jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/generator/PushPromiseGenerator.java

+8-8
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,17 @@ public int generatePushPromise(RetainableByteBuffer.Mutable accumulator, int str
4949
if (promisedStreamId < 0)
5050
throw new IllegalArgumentException("Invalid promised stream id: " + promisedStreamId);
5151

52-
int maxFrameSize = getMaxFrameSize();
53-
// The promised streamId space.
54-
int extraSpace = 4;
55-
maxFrameSize -= extraSpace;
56-
57-
RetainableByteBuffer hpack = encode(encoder, metaData, maxFrameSize);
52+
RetainableByteBuffer hpack = encode(encoder, metaData);
5853
ByteBuffer hpackByteBuffer = hpack.getByteBuffer();
59-
int hpackLength = hpackByteBuffer.position();
6054
BufferUtil.flipToFlush(hpackByteBuffer, 0);
55+
int hpackLength = hpackByteBuffer.remaining();
56+
57+
// No support for splitting in CONTINUATION frames,
58+
// also PushPromiseBodyParser does not support it.
6159

62-
int length = hpackLength + extraSpace;
60+
// The promised streamId length.
61+
int promisedStreamIdLength = 4;
62+
int length = hpackLength + promisedStreamIdLength;
6363
int flags = Flags.END_HEADERS;
6464

6565
generateHeader(accumulator, FrameType.PUSH_PROMISE, length, flags, streamId);

jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/parser/SettingsBodyParser.java

+4
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,10 @@ protected boolean onSettings(ByteBuffer buffer, Map<Integer, Integer> settings)
209209
if (maxFrameSize != null && (maxFrameSize < Frame.DEFAULT_MAX_SIZE || maxFrameSize > Frame.MAX_MAX_SIZE))
210210
return connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_settings_max_frame_size");
211211

212+
Integer maxHeaderListSize = settings.get(SettingsFrame.MAX_HEADER_LIST_SIZE);
213+
if (maxHeaderListSize != null && maxHeaderListSize <= 0)
214+
return connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_settings_max_header_list_size");
215+
212216
SettingsFrame frame = new SettingsFrame(settings, hasFlag(Flags.ACK));
213217
return onSettings(buffer, frame);
214218
}

jetty-core/jetty-http2/jetty-http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackContext.java

+1
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ public class HpackContext
116116
private static final StaticEntry[] __staticTable = new StaticEntry[STATIC_TABLE.length];
117117
public static final int STATIC_SIZE = STATIC_TABLE.length - 1;
118118
public static final int DEFAULT_MAX_TABLE_CAPACITY = 4096;
119+
public static final int DEFAULT_MAX_HEADER_LIST_SIZE = 4096;
119120

120121
static
121122
{

jetty-core/jetty-http2/jetty-http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java

+1
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ public HpackEncoder()
107107
_debug = LOG.isDebugEnabled();
108108
setMaxTableCapacity(HpackContext.DEFAULT_MAX_TABLE_CAPACITY);
109109
setTableCapacity(HpackContext.DEFAULT_MAX_TABLE_CAPACITY);
110+
setMaxHeaderListSize(HpackContext.DEFAULT_MAX_HEADER_LIST_SIZE);
110111
}
111112

112113
public int getMaxTableCapacity()

jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -300,10 +300,13 @@ protected Map<Integer, Integer> newSettings()
300300
int maxTableSize = getMaxDecoderTableCapacity();
301301
if (maxTableSize != HpackContext.DEFAULT_MAX_TABLE_CAPACITY)
302302
settings.put(SettingsFrame.HEADER_TABLE_SIZE, maxTableSize);
303+
settings.put(SettingsFrame.MAX_CONCURRENT_STREAMS, getMaxConcurrentStreams());
303304
int initialStreamRecvWindow = getInitialStreamRecvWindow();
304305
if (initialStreamRecvWindow != FlowControlStrategy.DEFAULT_WINDOW_SIZE)
305306
settings.put(SettingsFrame.INITIAL_WINDOW_SIZE, initialStreamRecvWindow);
306-
settings.put(SettingsFrame.MAX_CONCURRENT_STREAMS, getMaxConcurrentStreams());
307+
int maxFrameSize = getMaxFrameSize();
308+
if (maxFrameSize > Frame.DEFAULT_MAX_SIZE)
309+
settings.put(SettingsFrame.MAX_FRAME_SIZE, maxFrameSize);
307310
int maxHeadersSize = getHttpConfiguration().getRequestHeaderSize();
308311
if (maxHeadersSize > 0)
309312
settings.put(SettingsFrame.MAX_HEADER_LIST_SIZE, maxHeadersSize);
@@ -318,10 +321,10 @@ public Connection newConnection(Connector connector, EndPoint endPoint)
318321

319322
Generator generator = new Generator(connector.getByteBufferPool(), isUseOutputDirectByteBuffers(), getMaxHeaderBlockFragment());
320323
generator.getHpackEncoder().setMaxHeaderListSize(getHttpConfiguration().getResponseHeaderSize());
324+
321325
FlowControlStrategy flowControl = getFlowControlStrategyFactory().newFlowControlStrategy();
322326

323327
ServerParser parser = newServerParser(connector, getRateControlFactory().newRateControl(endPoint));
324-
parser.setMaxFrameSize(getMaxFrameSize());
325328
parser.setMaxSettingsKeys(getMaxSettingsKeys());
326329

327330
HTTP2ServerSession session = new HTTP2ServerSession(connector.getScheduler(), endPoint, parser, generator, listener, flowControl);

jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/HTTP2Test.java

+46-2
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,10 @@
4545
import org.eclipse.jetty.http2.frames.ResetFrame;
4646
import org.eclipse.jetty.http2.frames.SettingsFrame;
4747
import org.eclipse.jetty.http2.hpack.HpackException;
48+
import org.eclipse.jetty.http2.server.AbstractHTTP2ServerConnectionFactory;
4849
import org.eclipse.jetty.io.Content;
4950
import org.eclipse.jetty.server.Handler;
51+
import org.eclipse.jetty.server.HttpConfiguration;
5052
import org.eclipse.jetty.server.Request;
5153
import org.eclipse.jetty.server.Response;
5254
import org.eclipse.jetty.util.BufferUtil;
@@ -1051,7 +1053,7 @@ public void onClose(Session session, GoAwayFrame frame, Callback callback)
10511053
.put("custom", value);
10521054
MetaData.Request metaData = newRequest("GET", requestFields);
10531055
HeadersFrame request = new HeadersFrame(metaData, null, true);
1054-
session.newStream(request, new FuturePromise<>(), new Stream.Listener(){});
1056+
session.newStream(request, new FuturePromise<>(), new Stream.Listener() {});
10551057

10561058
// Test failure and close reason on client.
10571059
String closeReason = clientCloseReasonFuture.get(5, TimeUnit.SECONDS);
@@ -1125,7 +1127,7 @@ public void onClose(Session session, GoAwayFrame frame, Callback callback)
11251127
Session session = newClientSession(listener);
11261128
MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY);
11271129
HeadersFrame request = new HeadersFrame(metaData, null, true);
1128-
session.newStream(request, new FuturePromise<>(), new Stream.Listener(){});
1130+
session.newStream(request, new FuturePromise<>(), new Stream.Listener() {});
11291131

11301132
// Test failure and close reason on server.
11311133
String closeReason = serverCloseReasonFuture.get(5, TimeUnit.SECONDS);
@@ -1294,6 +1296,48 @@ public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
12941296
assertTrue(latch.await(5, TimeUnit.SECONDS));
12951297
}
12961298

1299+
@Test
1300+
public void testLargeRequestHeaders() throws Exception
1301+
{
1302+
int maxHeadersSize = 20 * 1024;
1303+
HttpConfiguration httpConfig = new HttpConfiguration();
1304+
httpConfig.setRequestHeaderSize(2 * maxHeadersSize);
1305+
start(new Handler.Abstract()
1306+
{
1307+
@Override
1308+
public boolean handle(Request request, Response response, Callback callback)
1309+
{
1310+
callback.succeeded();
1311+
return true;
1312+
}
1313+
}, httpConfig);
1314+
connector.getBean(AbstractHTTP2ServerConnectionFactory.class).setMaxFrameSize(17 * 1024);
1315+
http2Client.setMaxFrameSize(18 * 1024);
1316+
1317+
Session session = newClientSession(new Session.Listener() {});
1318+
1319+
CountDownLatch responseLatch = new CountDownLatch(1);
1320+
HttpFields.Mutable headers = HttpFields.build()
1321+
// Symbol "<" needs 15 bits to be Huffman encoded,
1322+
// while letters/numbers take typically less than
1323+
// 8 bits, and here we want to exceed maxHeadersSize.
1324+
.put("X-Large", "<".repeat(maxHeadersSize));
1325+
MetaData.Request request = newRequest("GET", headers);
1326+
session.newStream(new HeadersFrame(request, null, true), new Stream.Listener()
1327+
{
1328+
@Override
1329+
public void onHeaders(Stream stream, HeadersFrame frame)
1330+
{
1331+
assertTrue(frame.isEndStream());
1332+
MetaData.Response response = (MetaData.Response)frame.getMetaData();
1333+
assertEquals(HttpStatus.OK_200, response.getStatus());
1334+
responseLatch.countDown();
1335+
}
1336+
}).get(5, TimeUnit.SECONDS);
1337+
1338+
assertTrue(responseLatch.await(5, TimeUnit.SECONDS));
1339+
}
1340+
12971341
private static void sleep(long time)
12981342
{
12991343
try

jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/HttpClientTransportOverHTTP2Test.java

+1
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ private void testPropertiesAreForwarded(HTTP2Client http2Client, HttpClientTrans
145145
assertEquals(httpClient.getIdleTimeout(), http2Client.getIdleTimeout());
146146
assertEquals(httpClient.isUseInputDirectByteBuffers(), http2Client.isUseInputDirectByteBuffers());
147147
assertEquals(httpClient.isUseOutputDirectByteBuffers(), http2Client.isUseOutputDirectByteBuffers());
148+
assertEquals(httpClient.getRequestBufferSize(), http2Client.getMaxRequestHeadersSize());
148149
assertEquals(httpClient.getMaxResponseHeadersSize(), http2Client.getMaxResponseHeadersSize());
149150
}
150151
assertTrue(http2Client.isStopped());

jetty-core/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ReverseProxyTest.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,8 @@ public void testServerResponseHeadersTooLargeForServerConfiguration(HttpVersion
152152
@Override
153153
public boolean handle(Request request, Response response, Callback callback)
154154
{
155-
response.getHeaders().put("X-Large", "A".repeat(maxResponseHeadersSize));
155+
// Use "+" because in HTTP/2 is Huffman encoded in more than 8 bits.
156+
response.getHeaders().put("X-Large", "+".repeat(maxResponseHeadersSize));
156157

157158
// With HTTP/1.1, calling response.write() would fail the Handler callback
158159
// which would trigger ErrorHandler and result in a 500 to the proxy.

0 commit comments

Comments
 (0)