/* * 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;
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\n")) { // Test continues
output.clearTrace();
} elseif (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 publicvoid testHeaderLimits1x128() throws Exception { // Well within limits
doTestHeaderLimits(1, 128, FailureMode.NONE);
}
@Test publicvoid testHeaderLimits100x32() throws Exception { // Just within default maxHeaderCount // Note request has 4 standard headers
doTestHeaderLimits(96, 32, FailureMode.NONE);
}
@Test publicvoid testHeaderLimits8x1144() throws Exception { // Just within default maxHttpHeaderSize // per header overhead plus standard 3 headers
doTestHeaderLimits(7, 1144, FailureMode.NONE);
}
@Test publicvoid 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.CONNECTION_RESET);
}
privatevoid doTestHeaderLimits(int headerCount, int headerSize, int maxHeaderPayloadSize, FailureMode failMode) throws Exception {
doTestHeaderLimits(headerCount, headerSize, maxHeaderPayloadSize, 0, failMode);
}
privatevoid 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);
}
privatevoid 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 });
}
if (maxHeaderPayloadSize == -1) {
maxHeaderPayloadSize = output.getMaxFrameSize();
}
// Build the simple request byte[] frameHeader = newbyte[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));
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]\n", output.getTrace()); Assert.assertNull(e); break;
} 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 se) { // Expected on some platforms
} break;
}
}
}
privatevoid populateHeadersPayload(ByteBuffer headersPayload, List<String[]> customHeaders, String path) throws Exception {
MimeHeaders headers = new MimeHeaders();
headers.addValue(":method").setString("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) { thrownew Exception("Unable to build headers");
}
headersPayload.flip();
((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);
// 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]\n" + "3-WindowSize-[256]\n" + "3-HeadersStart\n" + "3-Header-[:status]-[200]\n" + "3-Header-[content-length]-[" + len + "]\n" + "3-Header-[date]-[" + DEFAULT_DATE + "]\n" + "3-HeadersEnd\n" + "3-Body-" + len + "\n" + "3-EndOfStream\n", 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]\n".equals(output.getTrace())) {
output.clearTrace();
parser.readFrame();
}
Assert.assertEquals("3-RST-[11]\n", output.getTrace()); 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;
}
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.