Add max http response header size configuration for tomcat and jetty
See gh-33553
This commit is contained in:
committed by
Moritz Halbritter
parent
ace31cd5b2
commit
93d46d11e9
@@ -0,0 +1,26 @@
|
||||
package smoketest.jetty.service;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import smoketest.jetty.util.RandomStringUtil;
|
||||
|
||||
@Component
|
||||
public class HttpHeaderService {
|
||||
|
||||
@Value("${server.jetty.max-http-response-header-size}")
|
||||
private int maxHttpResponseHeaderSize;
|
||||
|
||||
/**
|
||||
* generate a random byte array that
|
||||
* <ol>
|
||||
* <li>is longer than configured
|
||||
* <code>server.jetty.max-http-response-header-size</code></li>
|
||||
* <li>is url encoded by base 64 encode the random value</li>
|
||||
* </ol>
|
||||
* @return a base64 encoded string of random bytes
|
||||
*/
|
||||
public String getHeaderValue() {
|
||||
return RandomStringUtil.getRandomBase64EncodedString(maxHttpResponseHeaderSize + 1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package smoketest.jetty.util;
|
||||
|
||||
import java.util.Base64;
|
||||
import java.util.Random;
|
||||
|
||||
public class RandomStringUtil {
|
||||
|
||||
private RandomStringUtil() {
|
||||
}
|
||||
|
||||
public static String getRandomBase64EncodedString(int length) {
|
||||
byte[] responseHeader = new byte[length];
|
||||
new Random().nextBytes(responseHeader);
|
||||
return Base64.getEncoder().encodeToString(responseHeader);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,7 +16,9 @@
|
||||
|
||||
package smoketest.jetty.web;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import smoketest.jetty.service.HelloWorldService;
|
||||
import smoketest.jetty.service.HttpHeaderService;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
@@ -29,10 +31,21 @@ public class SampleController {
|
||||
@Autowired
|
||||
private HelloWorldService helloWorldService;
|
||||
|
||||
@Autowired
|
||||
private HttpHeaderService httpHeaderService;
|
||||
|
||||
@GetMapping("/")
|
||||
@ResponseBody
|
||||
public String helloWorld() {
|
||||
return this.helloWorldService.getHelloMessage();
|
||||
}
|
||||
|
||||
@GetMapping("/max-http-response-header")
|
||||
@ResponseBody
|
||||
public String maxHttpResponseHeader(HttpServletResponse response) {
|
||||
String headerValue = httpHeaderService.getHeaderValue();
|
||||
response.addHeader("x-max-header", headerValue);
|
||||
return this.helloWorldService.getHelloMessage();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
server.compression.enabled: true
|
||||
server.compression.min-response-size: 1
|
||||
server.max-http-request-header-size=1000
|
||||
server.jetty.threads.acceptors=2
|
||||
server.jetty.max-http-response-header-size=1000
|
||||
|
||||
@@ -22,9 +22,13 @@ import java.util.zip.GZIPInputStream;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.test.system.CapturedOutput;
|
||||
import org.springframework.boot.test.system.OutputCaptureExtension;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
@@ -32,6 +36,7 @@ import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.StreamUtils;
|
||||
import smoketest.jetty.util.RandomStringUtil;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -40,13 +45,19 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Andy Wilkinson
|
||||
* @author Florian Storz
|
||||
* @author Michael Weidmann
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||
@ExtendWith(OutputCaptureExtension.class)
|
||||
class SampleJettyApplicationTests {
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate restTemplate;
|
||||
|
||||
@Value("${server.max-http-request-header-size}")
|
||||
private int maxHttpRequestHeaderSize;
|
||||
|
||||
@Test
|
||||
void testHome() {
|
||||
ResponseEntity<String> entity = this.restTemplate.getForEntity("/", String.class);
|
||||
@@ -66,4 +77,22 @@ class SampleJettyApplicationTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMaxHttpResponseHeaderSize(CapturedOutput output) {
|
||||
ResponseEntity<String> entity = this.restTemplate.getForEntity("/max-http-response-header", String.class);
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
assertThat(output).contains(
|
||||
"org.eclipse.jetty.server.HttpChannel : handleException /max-http-response-header org.eclipse.jetty.http.BadMessageException: 500: Response header too large");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMaxHttpRequestHeaderSize() {
|
||||
String headerValue = RandomStringUtil.getRandomBase64EncodedString(maxHttpRequestHeaderSize + 1);
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add("x-max-request-header", headerValue);
|
||||
HttpEntity<?> httpEntity = new HttpEntity<>(headers);
|
||||
ResponseEntity<String> entity = this.restTemplate.exchange("/", HttpMethod.GET, httpEntity, String.class);
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package smoketest.tomcat.service;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import smoketest.tomcat.util.RandomStringUtil;
|
||||
|
||||
@Component
|
||||
public class HttpHeaderService {
|
||||
|
||||
@Value("${server.tomcat.max-http-response-header-size}")
|
||||
private int maxHttpResponseHeaderSize;
|
||||
|
||||
/**
|
||||
* generate a random byte array that
|
||||
* <ol>
|
||||
* <li>is longer than configured
|
||||
* <code>server.jetty.max-http-response-header-size</code></li>
|
||||
* <li>is url encoded by base 64 encode the random value</li>
|
||||
* </ol>
|
||||
* @return a base64 encoded string of random bytes
|
||||
*/
|
||||
public String getHeaderValue() {
|
||||
return RandomStringUtil.getRandomBase64EncodedString(maxHttpResponseHeaderSize + 1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package smoketest.tomcat.util;
|
||||
|
||||
import java.util.Base64;
|
||||
import java.util.Random;
|
||||
|
||||
public class RandomStringUtil {
|
||||
|
||||
private RandomStringUtil() {
|
||||
}
|
||||
|
||||
public static String getRandomBase64EncodedString(int length) {
|
||||
byte[] responseHeader = new byte[length];
|
||||
new Random().nextBytes(responseHeader);
|
||||
return Base64.getEncoder().encodeToString(responseHeader);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,12 +16,14 @@
|
||||
|
||||
package smoketest.tomcat.web;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import smoketest.tomcat.service.HelloWorldService;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import smoketest.tomcat.service.HttpHeaderService;
|
||||
|
||||
@Controller
|
||||
public class SampleController {
|
||||
@@ -29,10 +31,21 @@ public class SampleController {
|
||||
@Autowired
|
||||
private HelloWorldService helloWorldService;
|
||||
|
||||
@Autowired
|
||||
private HttpHeaderService httpHeaderService;
|
||||
|
||||
@GetMapping("/")
|
||||
@ResponseBody
|
||||
public String helloWorld() {
|
||||
return this.helloWorldService.getHelloMessage();
|
||||
}
|
||||
|
||||
@GetMapping("/max-http-response-header")
|
||||
@ResponseBody
|
||||
public String maxHttpResponseHeader(HttpServletResponse response) {
|
||||
String headerValue = httpHeaderService.getHeaderValue();
|
||||
response.addHeader("x-max-header", headerValue);
|
||||
return this.helloWorldService.getHelloMessage();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
server.compression.enabled: true
|
||||
server.compression.min-response-size: 1
|
||||
server.max-http-request-header-size=1000
|
||||
server.tomcat.connection-timeout=5s
|
||||
server.tomcat.max-http-response-header-size=1000
|
||||
|
||||
@@ -24,9 +24,13 @@ import org.apache.coyote.AbstractProtocol;
|
||||
import org.apache.coyote.ProtocolHandler;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.test.system.CapturedOutput;
|
||||
import org.springframework.boot.test.system.OutputCaptureExtension;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.boot.web.embedded.tomcat.TomcatWebServer;
|
||||
import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext;
|
||||
@@ -37,6 +41,7 @@ import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.StreamUtils;
|
||||
import smoketest.tomcat.util.RandomStringUtil;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -45,8 +50,11 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Andy Wilkinson
|
||||
* @author Florian Storz
|
||||
* @author Michael Weidmann
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||
@ExtendWith(OutputCaptureExtension.class)
|
||||
class SampleTomcatApplicationTests {
|
||||
|
||||
@Autowired
|
||||
@@ -55,6 +63,9 @@ class SampleTomcatApplicationTests {
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Value("${server.max-http-request-header-size}")
|
||||
private int maxHttpRequestHeaderSize;
|
||||
|
||||
@Test
|
||||
void testHome() {
|
||||
ResponseEntity<String> entity = this.restTemplate.getForEntity("/", String.class);
|
||||
@@ -83,4 +94,23 @@ class SampleTomcatApplicationTests {
|
||||
assertThat(timeout).isEqualTo(5000);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMaxHttpResponseHeaderSize(CapturedOutput output) {
|
||||
ResponseEntity<String> entity = this.restTemplate.getForEntity("/max-http-response-header", String.class);
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
assertThat(output).contains(
|
||||
"threw exception [Request processing failed: org.apache.coyote.http11.HeadersTooLargeException: An attempt was made to write more data to the response headers than there was room available in the buffer. Increase maxHttpHeaderSize on the connector or write less data into the response headers.]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMaxHttpRequestHeaderSize(CapturedOutput output) {
|
||||
String headerValue = RandomStringUtil.getRandomBase64EncodedString(maxHttpRequestHeaderSize + 1);
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add("x-max-request-header", headerValue);
|
||||
HttpEntity<?> httpEntity = new HttpEntity<>(headers);
|
||||
ResponseEntity<String> entity = this.restTemplate.exchange("/", HttpMethod.GET, httpEntity, String.class);
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
|
||||
assertThat(output).contains("java.lang.IllegalArgumentException: Request header is too large");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user