ttomcat-1778514358873.zip-extract/apache-tomcat-11.0.18-src/test/org/apache/coyote/http2/TestHttp2Limits.java

Path
ttomcat-1778514358873.zip-extract/apache-tomcat-11.0.18-src/test/org/apache/coyote/http2/TestHttp2Limits.java
Status
scanned
Type
file
Name
TestHttp2Limits.java
Extension
.java
Programming language
Java
Mime type
text/plain
File type
ASCII text, with CRLF line terminators
Tag

      
    
Rootfs path

      
    
Size
21910 (21.4 KB)
MD5
9c579216a718b5ef8ff19e11c469e5aa
SHA1
51f66906e06c397b1351dd9f6bf1a1c82618f36d
SHA256
cf01f31bc908285e0c25aca87919b3ce35159f43b71069611ebe0263283eb069
SHA512

      
    
SHA1_git
5ec4be0cd9ba444cc0ae19bd139d3ca2eb602e42
Is binary

      
    
Is text
True
Is archive

      
    
Is media

      
    
Is legal

      
    
Is manifest

      
    
Is readme

      
    
Is top level

      
    
Is key file

      
    
TestHttp2Limits.java | 21.4 KB |

/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.coyote.http2; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import java.util.Random; import org.hamcrest.Description; import org.hamcrest.MatcherAssert; import org.hamcrest.TypeSafeMatcher; import org.junit.Assert; import org.junit.Test; import org.apache.catalina.connector.Connector; import org.apache.coyote.http11.AbstractHttp11Protocol; import org.apache.coyote.http2.HpackEncoder.State; import org.apache.tomcat.util.http.Method; import org.apache.tomcat.util.http.MimeHeaders; import org.apache.tomcat.util.res.StringManager; public class TestHttp2Limits extends Http2TestBase { private static final StringManager sm = StringManager.getManager(TestHttp2Limits.class); @Test public void testSettingsOverheadLimits() throws Exception { http2Connect(); String errMsg = sm.getString("upgradeHandler.tooMuchOverhead", "\\p{XDigit}++").replace("[", "\\["); String overHeadMsgRegx = "0-Goaway-\\[1]-\\[11]-\\[" + errMsg + "]"; for (int i = 0; i < 100; i++) { try { sendSettings(0, false); parser.readFrame(); } catch (IOException ioe) { // Server closed connection before client has a chance to read // the Goaway frame. Treat this as a pass. return; } String trace = output.getTrace(); if (trace.equals("0-Settings-Ack ")) { // Test continues output.clearTrace(); } else if (trace.matches(overHeadMsgRegx)) { // Test passed return; } else { // Test failed Assert.fail("Unexpected output: " + output.getTrace()); } Thread.sleep(100); } // Test failed Assert.fail("Connection not closed down"); } @Test public void testHeaderLimits1x128() throws Exception { // Well within limits doTestHeaderLimits(1, 128, FailureMode.NONE); } @Test public void testHeaderLimits100x32() throws Exception { // Just within default maxHeaderCount // Note request has 4 standard headers doTestHeaderLimits(96, 32, FailureMode.NONE); } @Test public void testHeaderLimits101x32() throws Exception { // Just above default maxHeaderCount doTestHeaderLimits(97, 32, FailureMode.STREAM_RESET); } @Test public void testHeaderLimits20x32WithLimit10() throws Exception { // Check lower count limit is enforced doTestHeaderLimits(20, 32, -1, 10, Constants.DEFAULT_MAX_HEADER_SIZE, 0, FailureMode.STREAM_RESET); } @Test public void testHeaderLimits8x1144() throws Exception { // Just within default maxHttpHeaderSize // per header overhead plus standard 3 headers doTestHeaderLimits(7, 1144, FailureMode.NONE); } @Test public void testHeaderLimits8x1145() throws Exception { // Just above default maxHttpHeaderSize doTestHeaderLimits(7, 1145, FailureMode.STREAM_RESET); } @Test public void testHeaderLimits3x1024WithLimit2048() throws Exception { // Check lower size limit is enforced doTestHeaderLimits(3, 1024, -1, Constants.DEFAULT_MAX_HEADER_COUNT, 2 * 1024, 0, FailureMode.STREAM_RESET); } @Test public void testHeaderLimits1x12k() throws Exception { // Bug 60232 doTestHeaderLimits(1, 12 * 1024, FailureMode.STREAM_RESET); } @Test public void testHeaderLimits1x12kin1kChunks() throws Exception { // Bug 60232 doTestHeaderLimits(1, 12 * 1024, 1024, FailureMode.STREAM_RESET); } @Test public void testHeaderLimits1x12kin1kChunksThenNewRequest() throws Exception { // Bug 60232 doTestHeaderLimits(1, 12 * 1024, 1024, FailureMode.STREAM_RESET); output.clearTrace(); sendSimpleGetRequest(5); parser.readFrame(); parser.readFrame(); Assert.assertEquals(getSimpleResponseTrace(5), output.getTrace()); } @Test public void testHeaderLimits1x32k() throws Exception { // Bug 60232 doTestHeaderLimits(1, 32 * 1024, FailureMode.CONNECTION_RESET); } @Test public void testHeaderLimits1x32kin1kChunks() throws Exception { // Bug 60232 // 500ms per frame write delay to give server a chance to process the // stream reset and the connection reset before the request is fully // sent. doTestHeaderLimits(1, 32 * 1024, 1024, 500, FailureMode.STREAM_RESET_THEN_CONNECTION_RESET); } @Test public void testHeaderLimits1x128k() throws Exception { // Bug 60232 doTestHeaderLimits(1, 128 * 1024, FailureMode.CONNECTION_RESET); } @Test public void testHeaderLimits1x512k() throws Exception { // Bug 60232 doTestHeaderLimits(1, 512 * 1024, FailureMode.CONNECTION_RESET); } @Test public void testHeaderLimits10x512k() throws Exception { // Bug 60232 doTestHeaderLimits(10, 512 * 1024, FailureMode.CONNECTION_RESET); } private void doTestHeaderLimits(int headerCount, int headerSize, FailureMode failMode) throws Exception { doTestHeaderLimits(headerCount, headerSize, -1, failMode); } private void doTestHeaderLimits(int headerCount, int headerSize, int maxHeaderPayloadSize, FailureMode failMode) throws Exception { doTestHeaderLimits(headerCount, headerSize, maxHeaderPayloadSize, 0, failMode); } private void doTestHeaderLimits(int headerCount, int headerSize, int maxHeaderPayloadSize, int delayms, FailureMode failMode) throws Exception { doTestHeaderLimits(headerCount, headerSize, maxHeaderPayloadSize, Constants.DEFAULT_MAX_HEADER_COUNT, Constants.DEFAULT_MAX_HEADER_SIZE, delayms, failMode); } private void doTestHeaderLimits(int headerCount, int headerSize, int maxHeaderPayloadSize, int maxHeaderCount, int maxHeaderSize, int delayms, FailureMode failMode) throws Exception { // Build the custom headers List<String[]> customHeaders = new ArrayList<>(); StringBuilder headerValue = new StringBuilder(headerSize); // Does not need to be secure Random r = new Random(); for (int i = 0; i < headerSize; i++) { // Random lower case characters headerValue.append((char) ('a' + r.nextInt(26))); } String v = headerValue.toString(); for (int i = 0; i < headerCount; i++) { customHeaders.add(new String[] { "X-TomcatTest" + i, v }); } enableHttp2(); http2Protocol.setMaxHeaderCount(maxHeaderCount); ((AbstractHttp11Protocol<?>) http2Protocol.getHttp11Protocol()).setMaxHttpHeaderSize(maxHeaderSize); configureAndStartWebApplication(); openClientConnection(); doHttpUpgrade(); sendClientPreface(); validateHttp2InitialResponse(); if (maxHeaderPayloadSize == -1) { maxHeaderPayloadSize = output.getMaxFrameSize(); } // Build the simple request byte[] frameHeader = new byte[9]; // Assumes at least one custom header and that all headers are the same // length. These assumptions are valid for these tests. ByteBuffer headersPayload = ByteBuffer .allocate(200 + (int) (customHeaders.size() * customHeaders.iterator().next()[1].length() * 1.2)); populateHeadersPayload(headersPayload, customHeaders, "/simple"); Exception e = null; try { int written = 0; int left = headersPayload.limit() - written; while (left > 0) { int thisTime = Math.min(left, maxHeaderPayloadSize); populateFrameHeader(frameHeader, written, left, thisTime, 3); writeFrame(frameHeader, headersPayload, headersPayload.limit() - left, thisTime, delayms); left -= thisTime; written += thisTime; } } catch (IOException ioe) { e = ioe; } switch (failMode) { case NONE: { // Expect a normal response readSimpleGetResponse(); Assert.assertEquals(getSimpleResponseTrace(3), output.getTrace()); Assert.assertNull(e); break; } case STREAM_RESET: { // Expect a stream reset parser.readFrame(); Assert.assertEquals("3-RST-[11] ", output.getTrace()); Assert.assertNull(e); break; } case STREAM_RESET_THEN_CONNECTION_RESET: { // Expect a stream reset // On some platform / Connector combinations the TCP connection close // will be processed before the client gets a chance to read the // connection close frame which will trigger an // IOException when we try to read the frame. try { parser.readFrame(); Assert.assertEquals("3-RST-[11] ", output.getTrace()); output.clearTrace(); } catch (IOException ioe) { // Expected on some platforms } } //$FALL-THROUGH$ case CONNECTION_RESET: { // This message uses i18n and needs to be used in a regular // expression (since we don't know the connection ID). Generate the // string as a regular expression and then replace '[' and ']' with // the escaped values. String limitMessage = sm.getString("http2Parser.headerLimitSize", "\\p{XDigit}++", "3"); limitMessage = limitMessage.replace("[", "\\[").replace("]", "\\]"); // Connection reset. Connection ID will vary so use a pattern // On some platform / Connector combinations the TCP connection close // will be processed before the client gets a chance to read the // connection close frame which will trigger an // IOException when we try to read the frame. // Note: Some platforms will allow the read if if the write fails // above. try { parser.readFrame(); MatcherAssert.assertThat(output.getTrace(), RegexMatcher.matchesRegex("0-Goaway-\\[1\\]-\\[11\\]-\\[" + limitMessage + "\\]")); } catch (IOException ignore) { // Expected on some platforms } break; } } } private void populateHeadersPayload(ByteBuffer headersPayload, List<String[]> customHeaders, String path) throws Exception { MimeHeaders headers = new MimeHeaders(); headers.addValue(":method").setString(Method.GET); headers.addValue(":scheme").setString("http"); headers.addValue(":path").setString(path); headers.addValue(":authority").setString("localhost:" + getPort()); for (String[] customHeader : customHeaders) { headers.addValue(customHeader[0]).setString(customHeader[1]); } State state = hpackEncoder.encode(headers, headersPayload); if (state != State.COMPLETE) { throw new Exception("Unable to build headers"); } headersPayload.flip(); log.debug("Headers payload generated of size [" + headersPayload.limit() + "]"); } private void populateFrameHeader(byte[] frameHeader, int written, int left, int thisTime, int streamId) throws Exception { ByteUtil.setThreeBytes(frameHeader, 0, thisTime); if (written == 0) { frameHeader[3] = FrameType.HEADERS.getIdByte(); // Flags. End of stream frameHeader[4] = 0x01; } else { frameHeader[3] = FrameType.CONTINUATION.getIdByte(); } if (left == thisTime) { // Flags. End of headers frameHeader[4] = (byte) (frameHeader[4] + 0x04); } // Stream id ByteUtil.set31Bits(frameHeader, 5, streamId); } @Test public void testCookieLimit1() throws Exception { doTestCookieLimit(1, 0); } @Test public void testCookieLimit2() throws Exception { doTestCookieLimit(2, 0); } @Test public void testCookieLimit100() throws Exception { doTestCookieLimit(100, 0); } @Test public void testCookieLimit100WithLimit50() throws Exception { doTestCookieLimit(100, 50, 1); } @Test public void testCookieLimit200() throws Exception { doTestCookieLimit(200, 0); } @Test public void testCookieLimit201() throws Exception { doTestCookieLimit(201, 1); } private void doTestCookieLimit(int cookieCount, int failMode) throws Exception { doTestCookieLimit(cookieCount, Constants.DEFAULT_MAX_COOKIE_COUNT, failMode); } private void doTestCookieLimit(int cookieCount, int maxCookieCount, int failMode) throws Exception { enableHttp2(); Connector connector = getTomcatInstance().getConnector(); connector.setMaxCookieCount(maxCookieCount); configureAndStartWebApplication(); openClientConnection(); doHttpUpgrade(); sendClientPreface(); validateHttp2InitialResponse(); output.setTraceBody(true); byte[] frameHeader = new byte[9]; ByteBuffer headersPayload = ByteBuffer.allocate(8192); List<String[]> customHeaders = new ArrayList<>(); for (int i = 0; i < cookieCount; i++) { customHeaders.add(new String[] { "Cookie", "a" + cookieCount + "=b" + cookieCount }); } populateHeadersPayload(headersPayload, customHeaders, "/cookie"); populateFrameHeader(frameHeader, 0, headersPayload.limit(), headersPayload.limit(), 3); writeFrame(frameHeader, headersPayload); switch (failMode) { case 0: { parser.readFrame(); parser.readFrame(); parser.readFrame(); System.out.println(output.getTrace()); Assert.assertEquals(getCookieResponseTrace(3, cookieCount), output.getTrace()); break; } case 1: { // Check status is 400 parser.readFrame(); Assert.assertTrue(output.getTrace(), output.getTrace().startsWith("3-HeadersStart 3-Header-[:status]-[400]")); output.clearTrace(); // Check EOS followed by error page body parser.readFrame(); Assert.assertTrue(output.getTrace(), output.getTrace().startsWith("3-EndOfStream 3-Body-<!doctype")); break; } default: { Assert.fail("Unknown failure mode specified"); } } } @Test public void testPostWithTrailerHeadersDefaultLimit() throws Exception { doTestPostWithTrailerHeaders(Constants.DEFAULT_MAX_TRAILER_COUNT, Constants.DEFAULT_MAX_TRAILER_SIZE, FailureMode.NONE); } @Test public void testPostWithTrailerHeadersCount0() throws Exception { doTestPostWithTrailerHeaders(0, Constants.DEFAULT_MAX_TRAILER_SIZE, FailureMode.STREAM_RESET); } @Test public void testPostWithTrailerHeadersSize0() throws Exception { doTestPostWithTrailerHeaders(Constants.DEFAULT_MAX_TRAILER_COUNT, 0, FailureMode.CONNECTION_RESET); } private void doTestPostWithTrailerHeaders(int maxTrailerCount, int maxTrailerSize, FailureMode failMode) throws Exception { enableHttp2(); ((AbstractHttp11Protocol<?>) http2Protocol.getHttp11Protocol()).setAllowedTrailerHeaders(TRAILER_HEADER_NAME); http2Protocol.setMaxTrailerCount(maxTrailerCount); ((AbstractHttp11Protocol<?>) http2Protocol.getHttp11Protocol()).setMaxTrailerSize(maxTrailerSize); // Disable overhead protection for window update as it breaks some tests http2Protocol.setOverheadWindowUpdateThreshold(0); configureAndStartWebApplication(); openClientConnection(); doHttpUpgrade(); sendClientPreface(); validateHttp2InitialResponse(); byte[] headersFrameHeader = new byte[9]; ByteBuffer headersPayload = ByteBuffer.allocate(128); byte[] dataFrameHeader = new byte[9]; ByteBuffer dataPayload = ByteBuffer.allocate(256); byte[] trailerFrameHeader = new byte[9]; ByteBuffer trailerPayload = ByteBuffer.allocate(256); buildPostRequest(headersFrameHeader, headersPayload, false, dataFrameHeader, dataPayload, null, true, 3); buildTrailerHeaders(trailerFrameHeader, trailerPayload, 3); // Write the headers writeFrame(headersFrameHeader, headersPayload); // Body writeFrame(dataFrameHeader, dataPayload); // Trailers writeFrame(trailerFrameHeader, trailerPayload); switch (failMode) { case NONE: { parser.readFrame(); parser.readFrame(); parser.readFrame(); parser.readFrame(); String len = Integer.toString(256 + TRAILER_HEADER_VALUE.length()); Assert.assertEquals("0-WindowSize-[256] " + "3-WindowSize-[256] " + "3-HeadersStart " + "3-Header-[:status]-[200] " + "3-Header-[content-length]-[" + len + "] " + "3-Header-[date]-[" + DEFAULT_DATE + "] " + "3-HeadersEnd " + "3-Body-" + len + " " + "3-EndOfStream ", output.getTrace()); break; } case STREAM_RESET: { // NIO2 can sometimes send window updates depending timing skipWindowSizeFrames(); // Async I/O can sometimes result in a stream closed reset before // the enhance your calm reset if ("3-RST-[5] ".equals(output.getTrace())) { output.clearTrace(); parser.readFrame(); } Assert.assertEquals("3-RST-[11] ", output.getTrace()); break; } case STREAM_RESET_THEN_CONNECTION_RESET: { Assert.fail("Not used"); break; } case CONNECTION_RESET: { // NIO2 can sometimes send window updates depending timing skipWindowSizeFrames(); // This message uses i18n and needs to be used in a regular // expression (since we don't know the connection ID). Generate the // string as a regular expression and then replace '[' and ']' with // the escaped values. String limitMessage = sm.getString("http2Parser.headerLimitSize", "\\p{XDigit}++", "3"); limitMessage = limitMessage.replace("[", "\\[").replace("]", "\\]"); MatcherAssert.assertThat(output.getTrace(), RegexMatcher.matchesRegex("0-Goaway-\\[3\\]-\\[11\\]-\\[" + limitMessage + "\\]")); break; } } } private enum FailureMode { NONE, STREAM_RESET, CONNECTION_RESET, STREAM_RESET_THEN_CONNECTION_RESET, } private static class RegexMatcher extends TypeSafeMatcher<String> { private final String pattern; RegexMatcher(String pattern) { this.pattern = pattern; } @Override public void describeTo(Description description) { description.appendText("match to regular expression pattern [" + pattern + "]"); } @Override protected boolean matchesSafely(String item) { return item.matches(pattern); } public static RegexMatcher matchesRegex(String pattern) { return new RegexMatcher(pattern); } } }
Detected license expression
apache-2.0
Detected license expression (SPDX)
Apache-2.0
Percentage of license text
6.39
Copyrights

      
    
Holders

      
    
Authors

      
    
License detections License expression License expression SPDX
apache_2_0-4bde3f57-78aa-4201-96bf-531cba09e7de apache-2.0 Apache-2.0
URL Start line End line
http://www.apache.org/licenses/LICENSE-2.0 9 9