Skip to content

Commit 9cdfe25

Browse files
committed
Improvements to HTTP/2 overhead protection.
1 parent d6db22e commit 9cdfe25

File tree

5 files changed

+64
-27
lines changed

5 files changed

+64
-27
lines changed

java/org/apache/coyote/http2/Http2AsyncParser.java

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -284,36 +284,39 @@ public void completed(Long result, Void attachment) {
284284
readUnknownFrame(streamId, frameTypeId, flags, payloadSize, payload);
285285
}
286286
}
287-
// See if there is a new 9 byte header and continue parsing if possible
288-
if (payload.remaining() >= 9) {
289-
int position = payload.position();
290-
payloadSize = ByteUtil.getThreeBytes(payload, position);
291-
frameTypeId = ByteUtil.getOneByte(payload, position + 3);
292-
frameType = FrameType.valueOf(frameTypeId);
293-
flags = ByteUtil.getOneByte(payload, position + 4);
294-
streamId = ByteUtil.get31Bits(payload, position + 5);
295-
streamException = false;
296-
if (payload.remaining() - 9 >= payloadSize) {
297-
continueParsing = true;
298-
// Now go over frame header
299-
payload.position(payload.position() + 9);
300-
try {
301-
validateFrame(null, frameType, streamId, flags, payloadSize);
302-
} catch (StreamException e) {
303-
error = e;
304-
streamException = true;
305-
} catch (Http2Exception e) {
306-
error = e;
307-
continueParsing = false;
287+
if (!upgradeHandler.isOverheadLimitExceeded()) {
288+
// See if there is a new 9 byte header and continue parsing if possible
289+
if (payload.remaining() >= 9) {
290+
int position = payload.position();
291+
payloadSize = ByteUtil.getThreeBytes(payload, position);
292+
frameTypeId = ByteUtil.getOneByte(payload, position + 3);
293+
frameType = FrameType.valueOf(frameTypeId);
294+
flags = ByteUtil.getOneByte(payload, position + 4);
295+
streamId = ByteUtil.get31Bits(payload, position + 5);
296+
streamException = false;
297+
if (payload.remaining() - 9 >= payloadSize) {
298+
continueParsing = true;
299+
// Now go over frame header
300+
payload.position(payload.position() + 9);
301+
try {
302+
validateFrame(null, frameType, streamId, flags, payloadSize);
303+
} catch (StreamException e) {
304+
error = e;
305+
streamException = true;
306+
} catch (Http2Exception e) {
307+
error = e;
308+
continueParsing = false;
309+
}
308310
}
309311
}
310312
}
311313
} while (continueParsing);
312314
} catch (RuntimeException | IOException | Http2Exception e) {
313315
error = e;
314-
}
315-
if (payload.hasRemaining()) {
316-
socketWrapper.unRead(payload);
316+
} finally {
317+
if (payload.hasRemaining()) {
318+
socketWrapper.unRead(payload);
319+
}
317320
}
318321
}
319322
if (state == CompletionState.DONE) {

java/org/apache/coyote/http2/Http2Protocol.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,10 @@ public class Http2Protocol implements UpgradeProtocol {
5353
// Maximum amount of streams which can be concurrently executed over
5454
// a single connection
5555
static final int DEFAULT_MAX_CONCURRENT_STREAM_EXECUTION = 20;
56-
56+
// Default factor used when adjusting overhead count for overhead frames
5757
static final int DEFAULT_OVERHEAD_COUNT_FACTOR = 10;
58+
// Default factor used when adjusting overhead count for reset frames
59+
static final int DEFAULT_OVERHEAD_RESET_FACTOR = 50;
5860
// Not currently configurable. This makes the practical limit for
5961
// overheadCountFactor to be ~20. The exact limit will vary with traffic
6062
// patterns.
@@ -85,6 +87,7 @@ public class Http2Protocol implements UpgradeProtocol {
8587
private int maxHeaderCount = Constants.DEFAULT_MAX_HEADER_COUNT;
8688
private int maxTrailerCount = Constants.DEFAULT_MAX_TRAILER_COUNT;
8789
private int overheadCountFactor = DEFAULT_OVERHEAD_COUNT_FACTOR;
90+
private int overheadResetFactor = DEFAULT_OVERHEAD_RESET_FACTOR;
8891
private int overheadContinuationThreshold = DEFAULT_OVERHEAD_CONTINUATION_THRESHOLD;
8992
private int overheadDataThreshold = DEFAULT_OVERHEAD_DATA_THRESHOLD;
9093
private int overheadWindowUpdateThreshold = DEFAULT_OVERHEAD_WINDOW_UPDATE_THRESHOLD;
@@ -290,6 +293,20 @@ public void setOverheadCountFactor(int overheadCountFactor) {
290293
}
291294

292295

296+
public int getOverheadResetFactor() {
297+
return overheadResetFactor;
298+
}
299+
300+
301+
public void setOverheadResetFactor(int overheadResetFactor) {
302+
if (overheadResetFactor < 0) {
303+
this.overheadResetFactor = 0;
304+
} else {
305+
this.overheadResetFactor = overheadResetFactor;
306+
}
307+
}
308+
309+
293310
public int getOverheadContinuationThreshold() {
294311
return overheadContinuationThreshold;
295312
}

java/org/apache/coyote/http2/Http2UpgradeHandler.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ public SocketState upgradeDispatch(SocketEvent status) {
377377
stream.close(se);
378378
}
379379
} finally {
380-
if (overheadCount.get() > 0) {
380+
if (isOverheadLimitExceeded()) {
381381
throw new ConnectionException(
382382
sm.getString("upgradeHandler.tooMuchOverhead", connectionId),
383383
Http2Error.ENHANCE_YOUR_CALM);
@@ -1368,6 +1368,11 @@ private void updateOverheadCount(FrameType frameType, int increment) {
13681368
}
13691369

13701370

1371+
boolean isOverheadLimitExceeded() {
1372+
return overheadCount.get() > 0;
1373+
}
1374+
1375+
13711376
// ----------------------------------------------- Http2Parser.Input methods
13721377

13731378
@Override
@@ -1630,6 +1635,7 @@ public void reset(int streamId, long errorCode) throws Http2Exception {
16301635
log.debug(sm.getString("upgradeHandler.reset.receive", getConnectionId(), Integer.toString(streamId),
16311636
Long.toString(errorCode)));
16321637
}
1638+
increaseOverheadCount(FrameType.RST, getProtocol().getOverheadResetFactor());
16331639
AbstractNonZeroStream abstractNonZeroStream = getAbstractNonZeroStream(streamId, true);
16341640
abstractNonZeroStream.checkState(FrameType.RST);
16351641
if (abstractNonZeroStream instanceof Stream) {
@@ -1763,6 +1769,7 @@ public void incrementWindowSize(int streamId, int increment) throws Http2Excepti
17631769

17641770
@Override
17651771
public void priorityUpdate(int prioritizedStreamID, Priority p) throws Http2Exception {
1772+
increaseOverheadCount(FrameType.PRIORITY_UPDATE);
17661773
AbstractNonZeroStream abstractNonZeroStream = getAbstractNonZeroStream(prioritizedStreamID, true);
17671774
if (abstractNonZeroStream instanceof Stream) {
17681775
Stream stream = (Stream) abstractNonZeroStream;

webapps/docs/changelog.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@
177177
<fix>
178178
Align validation of HTTP trailer fields with standard fields. (markt)
179179
</fix>
180+
<fix>
181+
Improvements to HTTP/2 overhead protection. (markt)
182+
</fix>
180183
</changelog>
181184
</subsection>
182185
<subsection name="Jasper">

webapps/docs/config/http2.xml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,14 +139,21 @@
139139
count starts at <code>-10 * overheadCountFactor</code>. The count is
140140
decreased by 20 for each data frame sent or received and each headers frame
141141
received. The count is increased by the <code>overheadCountFactor</code>
142-
for each setting received, priority frame received and ping received. If
142+
for each setting, priority, priority update and ping frame received. If
143143
the overhead count exceeds zero, the connection is closed. A value of less
144144
than <code>1</code> disables this protection. In normal usage a value of
145145
approximately <code>20</code> or higher will close the connection before
146146
any streams can complete. If not specified, a default value of
147147
<code>10</code> will be used.</p>
148148
</attribute>
149149

150+
<attribute name="overheadResetFactor" required="false">
151+
<p>The amount by which the overhead count (see
152+
<strong>overheadCountFactor</strong>) will be increased for each reset
153+
frame received. If not specified, a default value of <code>50</code> will
154+
be used. A value of less than zero will be treated as zero.</p>
155+
</attribute>
156+
150157
<attribute name="overheadDataThreshold" required="false">
151158
<p>The threshold below which the average payload size of the current and
152159
previous non-final <code>DATA</code> frames will trigger an increase in

0 commit comments

Comments
 (0)