From 04800115758dd64886e07e7bad9529c55b8f5c7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20R=C3=BCtter?= Date: Wed, 7 May 2025 14:39:11 +0200 Subject: [PATCH 1/8] FELIX-6774 `org.apache.felix.http.jetty.maxFormSize` not enforced - Introducing two new config properties, `org.apache.felix.http.jetty.requestSizeLimit` and `org.apache.felix.http.jetty.responseSizeLimit` - If either one is configured, the SizeLimitHandler will be configured - -1 is the default, which means unlimited - Do we still need to set `setMaxFormContentSize` now? - Updated README.md --- http/README.md | 110 +++++++++--------- .../internal/ConfigMetaTypeProvider.java | 16 ++- .../http/jetty/internal/JettyConfig.java | 18 ++- .../http/jetty/internal/JettyService.java | 12 ++ .../http/jetty/it/JettyMaxFormSizeIT.java | 41 ++++--- 5 files changed, 127 insertions(+), 70 deletions(-) diff --git a/http/README.md b/http/README.md index 2940b01227..d6b65bc8e7 100644 --- a/http/README.md +++ b/http/README.md @@ -387,61 +387,63 @@ The service can both be configured using OSGi environment properties and using C this service is `"org.apache.felix.http"`. If you use both methods, Configuration Admin takes precedence. The following properties can be used (some legacy property names still exist but are not documented here on purpose). As properties might change over time, the actual list of properties can be found [here for the Jetty 12 bundle](https://github.com/apache/felix-dev/blob/master/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java) and [here for the Jetty 11 bundle](https://github.com/apache/felix-dev/blob/master/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java). -| Property | Description | -|----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `org.apache.felix.http.host` | Host name or IP Address of the interface to listen on. The default is `null` causing Jetty to listen on all interfaces. | -| `org.osgi.service.http.port` | The port used for servlets and resources available via HTTP. The default is `8080`. See [port settings below](#http-port-settings) for additional information. A negative port number has the same effect as setting `org.apache.felix.http.enable` to `false`. | -| `org.osgi.service.http.port.secure` | The port used for servlets and resources available via HTTPS. The default is `8443`. See [port settings below](#http-port-settings) for additional information. A negative port number has the same effect as setting `org.apache.felix.https.enable` to `false`. | -| `org.apache.felix.http.context_path` | The servlet Context Path to use for the Http Service. If this property is not configured it defaults to "/". This must be a valid path starting with a slash and not ending with a slash (unless it is the root context). | -| `org.apache.felix.http.timeout` | Connection timeout in milliseconds. The default is `60000` (60 seconds). | -| `org.apache.felix.http.session.timeout` | Allows for the specification of the Session life time as a number of minutes. This property serves the same purpose as the `session-timeout` element in a Web Application descriptor. The default is "0" (zero) for no timeout at all. | -| `org.apache.felix.http.enable` | Flag to enable the use of HTTP. The default is `true`. | -| `org.apache.felix.https.enable` | Flag to enable the user of HTTPS. The default is `false`. | -| `org.apache.felix.https.keystore` | The name of the file containing the keystore. | -| `org.apache.felix.https.keystore.password` | The password for the keystore. | -| `org.apache.felix.https.keystore.key.password` | The password for the key in the keystore. | -| `org.apache.felix.https.truststore` | The name of the file containing the truststore. | -| `org.apache.felix.https.truststore.type` | The type of truststore to use. The default is `JKS`. | -| `org.apache.felix.https.truststore.password` | The password for the truststore. | -| `org.apache.felix.https.jetty.ciphersuites.excluded` | Configures comma-separated list of SSL cipher suites to *exclude*. Default is `null`, meaning that no cipher suite is excluded. | -| `org.apache.felix.https.jetty.ciphersuites.included` | Configures comma-separated list of SSL cipher suites to *include*. Default is `null`, meaning that the default cipher suites are used. | -| `org.apache.felix.https.jetty.protocols.excluded` | Configures comma-separated list of SSL protocols (e.g. SSLv3, TLSv1.0, TLSv1.1, TLSv1.2) to *exclude*. Default is `null`, meaning that no protocol is excluded. | -| `org.apache.felix.https.jetty.protocols.included` | Configures comma-separated list of SSL protocols to *include*. Default is `null`, meaning that the default protocols are used. | -| `org.apache.felix.https.clientcertificate` | Flag to determine if the HTTPS protocol requires, wants or does not use client certificates. Legal values are `needs`, `wants` and `none`. The default is `none`. | -| `org.apache.felix.http.jetty.headerBufferSize` | Size of the buffer for request and response headers, in bytes. Default is 16 KB. | -| `org.apache.felix.http.jetty.requestBufferSize` | Size of the buffer for requests not fitting the header buffer, in bytes. Default is 8 KB. | -| `org.apache.felix.http.jetty.responseBufferSize` | Size of the buffer for responses, in bytes. Default is 24 KB. | -| `org.apache.felix.http.jetty.maxFormSize` | The maximum size accepted for a form post, in bytes. Defaults to 200 KB. | -| `org.apache.felix.http.mbeans` | If `true`, enables the MBean server functionality. The default is `false`. | -| `org.apache.felix.http.jetty.sendServerHeader` | If `false`, the `Server` HTTP header is no longer included in responses. The default is `false`. | -| `org.eclipse.jetty.servlet.SessionCookie` | Name of the cookie used to transport the Session ID. The default is `JSESSIONID`. | -| `org.eclipse.jetty.servlet.SessionURL` | Name of the request parameter to transport the Session ID. The default is `jsessionid`. | -| `org.eclipse.jetty.servlet.SessionDomain` | Domain to set on the session cookie. The default is `null`. | -| `org.eclipse.jetty.servlet.SessionPath` | The path to set on the session cookie. The default is the configured session context path ("/"). | -| `org.eclipse.jetty.servlet.MaxAge` | The maximum age value to set on the cookie. The default is "-1". | +| Property | Description | +|----------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `org.apache.felix.http.host` | Host name or IP Address of the interface to listen on. The default is `null` causing Jetty to listen on all interfaces. | +| `org.osgi.service.http.port` | The port used for servlets and resources available via HTTP. The default is `8080`. See [port settings below](#http-port-settings) for additional information. A negative port number has the same effect as setting `org.apache.felix.http.enable` to `false`. | +| `org.osgi.service.http.port.secure` | The port used for servlets and resources available via HTTPS. The default is `8443`. See [port settings below](#http-port-settings) for additional information. A negative port number has the same effect as setting `org.apache.felix.https.enable` to `false`. | +| `org.apache.felix.http.context_path` | The servlet Context Path to use for the Http Service. If this property is not configured it defaults to "/". This must be a valid path starting with a slash and not ending with a slash (unless it is the root context). | +| `org.apache.felix.http.timeout` | Connection timeout in milliseconds. The default is `60000` (60 seconds). | +| `org.apache.felix.http.session.timeout` | Allows for the specification of the Session life time as a number of minutes. This property serves the same purpose as the `session-timeout` element in a Web Application descriptor. The default is "0" (zero) for no timeout at all. | +| `org.apache.felix.http.enable` | Flag to enable the use of HTTP. The default is `true`. | +| `org.apache.felix.https.enable` | Flag to enable the user of HTTPS. The default is `false`. | +| `org.apache.felix.https.keystore` | The name of the file containing the keystore. | +| `org.apache.felix.https.keystore.password` | The password for the keystore. | +| `org.apache.felix.https.keystore.key.password` | The password for the key in the keystore. | +| `org.apache.felix.https.truststore` | The name of the file containing the truststore. | +| `org.apache.felix.https.truststore.type` | The type of truststore to use. The default is `JKS`. | +| `org.apache.felix.https.truststore.password` | The password for the truststore. | +| `org.apache.felix.https.jetty.ciphersuites.excluded` | Configures comma-separated list of SSL cipher suites to *exclude*. Default is `null`, meaning that no cipher suite is excluded. | +| `org.apache.felix.https.jetty.ciphersuites.included` | Configures comma-separated list of SSL cipher suites to *include*. Default is `null`, meaning that the default cipher suites are used. | +| `org.apache.felix.https.jetty.protocols.excluded` | Configures comma-separated list of SSL protocols (e.g. SSLv3, TLSv1.0, TLSv1.1, TLSv1.2) to *exclude*. Default is `null`, meaning that no protocol is excluded. | +| `org.apache.felix.https.jetty.protocols.included` | Configures comma-separated list of SSL protocols to *include*. Default is `null`, meaning that the default protocols are used. | +| `org.apache.felix.https.clientcertificate` | Flag to determine if the HTTPS protocol requires, wants or does not use client certificates. Legal values are `needs`, `wants` and `none`. The default is `none`. | +| `org.apache.felix.http.jetty.headerBufferSize` | Size of the buffer for request and response headers, in bytes. Default is 16 KB. | +| `org.apache.felix.http.jetty.requestBufferSize` | Size of the buffer for requests not fitting the header buffer, in bytes. Default is 8 KB. | +| `org.apache.felix.http.jetty.responseBufferSize` | Size of the buffer for responses, in bytes. Default is 24 KB. | +| `org.apache.felix.http.jetty.maxFormSize` | The maximum size accepted for a form post, in bytes. Defaults to 200 KB. Deprecated in favor of `org.apache.felix.http.jetty.requestSizeLimit` since Jetty12 1.0.28. | +| `org.apache.felix.http.jetty.requestBufferSize` | Maximum size of the request body for submitted form/multipart content. Default is unlimited. Added in Jetty12 1.0.30 as replacement of `org.apache.felix.http.jetty.maxFormSize`. | +| `org.apache.felix.http.jetty.responseBufferSize` | Maximum size of a response. Default is unlimited. Default is unlimited. Added in Jetty12 1.0.30. | +| `org.apache.felix.http.mbeans` | If `true`, enables the MBean server functionality. The default is `false`. | +| `org.apache.felix.http.jetty.sendServerHeader` | If `false`, the `Server` HTTP header is no longer included in responses. The default is `false`. | +| `org.eclipse.jetty.servlet.SessionCookie` | Name of the cookie used to transport the Session ID. The default is `JSESSIONID`. | +| `org.eclipse.jetty.servlet.SessionURL` | Name of the request parameter to transport the Session ID. The default is `jsessionid`. | +| `org.eclipse.jetty.servlet.SessionDomain` | Domain to set on the session cookie. The default is `null`. | +| `org.eclipse.jetty.servlet.SessionPath` | The path to set on the session cookie. The default is the configured session context path ("/"). | +| `org.eclipse.jetty.servlet.MaxAge` | The maximum age value to set on the cookie. The default is "-1". | | `org.eclipse.jetty.UriComplianceMode` | The URI compliance mode to set. The default is [DEFAULT](https://eclipse.dev/jetty/javadoc/jetty-12/org/eclipse/jetty/http/UriCompliance.html#DEFAULT). See [documentation](https://eclipse.dev/jetty/documentation/jetty-12/programming-guide/index.html#pg-server-compliance-uri.) and [possible modes](https://github.com/jetty/jetty.project/blob/jetty-12.0.x/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/UriCompliance.java#L186C107-L186C113). Use with caution, as it may have [security implications](https://github.com/apache/felix-dev/pull/308#issuecomment-2438913766). | -| `org.apache.felix.proxy.load.balancer.connection.enable` | Set this to `true` when running Felix HTTP behind a (offloading) proxy or load balancer which rewrites the requests. The default is `false`. | -| `org.apache.felix.http.runtime.init.` | Properties starting with this prefix are added as service registration properties to the HttpServiceRuntime service. The prefix is removed for the property name. | -| `org.apache.felix.jetty.gziphandler.enable` | Whether the server should use a server-wide gzip handler. Default is false. | -| `org.apache.felix.jetty.gzip.minGzipSize` | The minimum response size to trigger dynamic compression. Default is GzipHandler.DEFAULT_MIN_GZIP_SIZE. | -| `org.apache.felix.jetty.gzip.inflateBufferSize` | The size in bytes of the buffer to inflate compressed request, or <= 0 for no inflation. Default is -1. | -| `org.apache.felix.jetty.gzip.syncFlush` | True if Deflater#SYNC_FLUSH should be used, else Deflater#NO_FLUSH will be used. Default is false. | -| `org.apache.felix.jetty.gzip.includedMethods` | The additional http methods to include in compression. Default is none. | -| `org.apache.felix.jetty.gzip.excludedMethods` | The additional http methods to exclude in compression. Default is none. | -| `org.apache.felix.jetty.gzip.includedPaths` | The additional path specs to include. Inclusion takes precedence over exclusion. Default is none. | -| `org.apache.felix.jetty.gzip.excludedPaths` | The additional path specs to exclude. Inclusion takes precedence over exclusion. Default is none. | -| `org.apache.felix.jetty.gzip.includedMimeTypes` | The included mime types. Inclusion takes precedence over exclusion. Default is none. | -| `org.apache.felix.jetty.gzip.excludedMimeTypes` | The excluded mime types. Inclusion takes precedence over exclusion. Default is none. | -| `org.apache.felix.http2.enable` | Whether to enable HTTP/2. Default is false. | -| `org.apache.felix.jetty.http2.maxConcurrentStreams` | The max number of concurrent streams per connection. Default is 128. | -| `org.apache.felix.jetty.http2.initialStreamRecvWindow` | The initial stream receive window (client to server). Default is 524288. | -| `org.apache.felix.jetty.http2.initialSessionRecvWindow` | The initial session receive window (client to server). Default is 1048576. | -| `org.apache.felix.jetty.alpn.protocols` | The ALPN protocols to consider. Default is h2, http/1.1. | -| `org.apache.felix.jetty.alpn.defaultProtocol` | The default protocol when negotiation fails. Default is http/1.1. | -| `org.apache.felix.jakarta.websocket.enable` | Enables Jakarta websocket support. Default is false. | -| `org.apache.felix.jetty.websocket.enable` | Enables Jetty websocket support. Default is false. | -| `org.apache.felix.http.jetty.threadpool.max` | The maximum number of threads in the Jetty thread pool. Default is unlimited. Works for both platform threads and virtual threads (Jetty 12 only). | -| `org.apache.felix.http.jetty.virtualthreads.enable` | Enables using virtual threads in Jetty 12 (JDK 21 required). Default is false. When enabled, `org.apache.felix.http.jetty.threadpool.max` is used for a bounded virtual thread pool. | +| `org.apache.felix.proxy.load.balancer.connection.enable` | Set this to `true` when running Felix HTTP behind a (offloading) proxy or load balancer which rewrites the requests. The default is `false`. | +| `org.apache.felix.http.runtime.init.` | Properties starting with this prefix are added as service registration properties to the HttpServiceRuntime service. The prefix is removed for the property name. | +| `org.apache.felix.jetty.gziphandler.enable` | Whether the server should use a server-wide gzip handler. Default is false. | +| `org.apache.felix.jetty.gzip.minGzipSize` | The minimum response size to trigger dynamic compression. Default is GzipHandler.DEFAULT_MIN_GZIP_SIZE. | +| `org.apache.felix.jetty.gzip.inflateBufferSize` | The size in bytes of the buffer to inflate compressed request, or <= 0 for no inflation. Default is -1. | +| `org.apache.felix.jetty.gzip.syncFlush` | True if Deflater#SYNC_FLUSH should be used, else Deflater#NO_FLUSH will be used. Default is false. | +| `org.apache.felix.jetty.gzip.includedMethods` | The additional http methods to include in compression. Default is none. | +| `org.apache.felix.jetty.gzip.excludedMethods` | The additional http methods to exclude in compression. Default is none. | +| `org.apache.felix.jetty.gzip.includedPaths` | The additional path specs to include. Inclusion takes precedence over exclusion. Default is none. | +| `org.apache.felix.jetty.gzip.excludedPaths` | The additional path specs to exclude. Inclusion takes precedence over exclusion. Default is none. | +| `org.apache.felix.jetty.gzip.includedMimeTypes` | The included mime types. Inclusion takes precedence over exclusion. Default is none. | +| `org.apache.felix.jetty.gzip.excludedMimeTypes` | The excluded mime types. Inclusion takes precedence over exclusion. Default is none. | +| `org.apache.felix.http2.enable` | Whether to enable HTTP/2. Default is false. | +| `org.apache.felix.jetty.http2.maxConcurrentStreams` | The max number of concurrent streams per connection. Default is 128. | +| `org.apache.felix.jetty.http2.initialStreamRecvWindow` | The initial stream receive window (client to server). Default is 524288. | +| `org.apache.felix.jetty.http2.initialSessionRecvWindow` | The initial session receive window (client to server). Default is 1048576. | +| `org.apache.felix.jetty.alpn.protocols` | The ALPN protocols to consider. Default is h2, http/1.1. | +| `org.apache.felix.jetty.alpn.defaultProtocol` | The default protocol when negotiation fails. Default is http/1.1. | +| `org.apache.felix.jakarta.websocket.enable` | Enables Jakarta websocket support. Default is false. | +| `org.apache.felix.jetty.websocket.enable` | Enables Jetty websocket support. Default is false. | +| `org.apache.felix.http.jetty.threadpool.max` | The maximum number of threads in the Jetty thread pool. Default is unlimited. Works for both platform threads and virtual threads (Jetty 12 only). | +| `org.apache.felix.http.jetty.virtualthreads.enable` | Enables using virtual threads in Jetty 12 (JDK 21 required). Default is false. When enabled, `org.apache.felix.http.jetty.threadpool.max` is used for a bounded virtual thread pool. | ### Multiple Servers diff --git a/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/ConfigMetaTypeProvider.java b/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/ConfigMetaTypeProvider.java index 817ec97ad8..c133eb2df5 100644 --- a/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/ConfigMetaTypeProvider.java +++ b/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/ConfigMetaTypeProvider.java @@ -203,11 +203,23 @@ public ObjectClassDefinition getObjectClassDefinition( String id, String locale bundle.getBundleContext().getProperty(JettyConfig.FELIX_JETTY_RESPONSE_BUFFER_SIZE))); adList.add(new AttributeDefinitionImpl(JettyConfig.FELIX_JETTY_MAX_FORM_SIZE, - "Maximum Form Size", - "Size of Body for submitted form content. Default is 200KB.", + "Maximum Form Size in bytes", + "Size of Body for submitted form content. Default is 200KB. Deprecated in favor of `org.apache.felix.http.jetty.requestSizeLimit`", 204800, bundle.getBundleContext().getProperty(JettyConfig.FELIX_JETTY_MAX_FORM_SIZE))); + adList.add(new AttributeDefinitionImpl(JettyConfig.FELIX_JETTY_REQUEST_SIZE_LIMIT, + "Maximum request size in bytes", + "Maximum size of the request body for submitted form/multipart content. Default is unlimited.", + 204800, + bundle.getBundleContext().getProperty(JettyConfig.FELIX_JETTY_REQUEST_SIZE_LIMIT))); + + adList.add(new AttributeDefinitionImpl(JettyConfig.FELIX_JETTY_RESPONSE_SIZE_LIMIT, + "Maximum response size in bytes", + "Maximum size of a response. Default is unlimited.", + 204800, + bundle.getBundleContext().getProperty(JettyConfig.FELIX_JETTY_RESPONSE_SIZE_LIMIT))); + adList.add(new AttributeDefinitionImpl(JettyConfig.FELIX_HTTP_PATH_EXCLUSIONS, "Path Exclusions", "Contains a list of context path prefixes. If a Web Application Bundle is started with a context path matching any " + diff --git a/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java b/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java index a04eb85a60..056cf0f1b4 100644 --- a/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java +++ b/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java @@ -106,9 +106,15 @@ public final class JettyConfig /** Felix specific property to configure the request buffer size. Default is 24KB */ public static final String FELIX_JETTY_RESPONSE_BUFFER_SIZE = "org.apache.felix.http.jetty.responseBufferSize"; - /** Felix specific property to configure the max form size. Default is 200KB */ + /** Felix specific property to configure the max form size. Default is 200KB. This is deprecated in favor of `org.apache.felix.http.jetty.requestSizeLimit` */ public static final String FELIX_JETTY_MAX_FORM_SIZE = "org.apache.felix.http.jetty.maxFormSize"; + /** Felix specific property to configure the request size limit. Default is unlimited. See https://jetty.org/docs/jetty/12/programming-guide/server/http.html#handler-use-size-limit */ + public static final String FELIX_JETTY_REQUEST_SIZE_LIMIT = "org.apache.felix.http.jetty.requestSizeLimit"; + + /** Felix specific property to configure the response size limit. Default is unlimited. See https://jetty.org/docs/jetty/12/programming-guide/server/http.html#handler-use-size-limit */ + public static final String FELIX_JETTY_RESPONSE_SIZE_LIMIT = "org.apache.felix.http.jetty.responseSizeLimit"; + /** Felix specific property to enable Jetty MBeans. Valid values are "true", "false". Default is false */ public static final String FELIX_HTTP_MBEANS = "org.apache.felix.http.mbeans"; @@ -493,6 +499,16 @@ public int getMaxFormSize() return getIntProperty(FELIX_JETTY_MAX_FORM_SIZE, 200 * 1024); } + public int getRequestSizeLimit() + { + return getIntProperty(FELIX_JETTY_REQUEST_SIZE_LIMIT, -1); + } + + public int getResponseSizeLimit() + { + return getIntProperty(FELIX_JETTY_RESPONSE_SIZE_LIMIT, -1); + } + /** * Returns the configured session timeout in minutes or zero if not * configured. diff --git a/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/JettyService.java b/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/JettyService.java index efe4f8fb83..5e1a66c59d 100644 --- a/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/JettyService.java +++ b/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/JettyService.java @@ -56,6 +56,7 @@ import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.SslConnectionFactory; import org.eclipse.jetty.server.handler.ContextHandlerCollection; +import org.eclipse.jetty.server.handler.SizeLimitHandler; import org.eclipse.jetty.server.handler.StatisticsHandler; import org.eclipse.jetty.server.handler.gzip.GzipHandler; import org.eclipse.jetty.session.HouseKeeper; @@ -322,8 +323,19 @@ private void initializeJetty() throws Exception final ServletHolder holder = new ServletHolder(this.controller.createDispatcherServlet()); holder.setAsyncSupported(true); context.addServlet(holder, "/*"); + + // Not sure if this does anything still, since we're using async form API's + // see https://stackoverflow.com/a/78659566 context.setMaxFormContentSize(this.config.getMaxFormSize()); + int requestSizeLimit = this.config.getRequestSizeLimit(); + int responseSizeLimit = this.config.getResponseSizeLimit(); + if (requestSizeLimit > -1 || responseSizeLimit > -1) { + // Use SizeLimitHandler to limit the size of the request body and response + // -1 is unlimited + context.setHandler(new SizeLimitHandler(requestSizeLimit, responseSizeLimit)); + } + if (this.config.isRegisterMBeans()) { this.mbeanServerTracker = new MBeanServerTracker(this.context, this.server); diff --git a/http/jetty12/src/test/java/org/apache/felix/http/jetty/it/JettyMaxFormSizeIT.java b/http/jetty12/src/test/java/org/apache/felix/http/jetty/it/JettyMaxFormSizeIT.java index 16b60272c9..1791d0cab1 100644 --- a/http/jetty12/src/test/java/org/apache/felix/http/jetty/it/JettyMaxFormSizeIT.java +++ b/http/jetty12/src/test/java/org/apache/felix/http/jetty/it/JettyMaxFormSizeIT.java @@ -79,21 +79,25 @@ protected Option[] additionalOptions() throws IOException { protected Option felixHttpConfig(int httpPort) { return newConfiguration("org.apache.felix.http") .put("org.osgi.service.http.port", httpPort) - .put("org.apache.felix.http.jetty.maxFormSize", LIMIT_IN_BYTES) // 10 bytes limit + .put("org.apache.felix.http.jetty.requestSizeLimit", LIMIT_IN_BYTES) // 10 bytes limit for the request + .put("org.apache.felix.http.jetty.responseSizeLimit", LIMIT_IN_BYTES) // 10 bytes limit for the response .asOption(); } @Before public void setup(){ assertNotNull(bundleContext); - bundleContext.registerService(Servlet.class, new HelloWorldServlet(), new Hashtable<>(Map.of( - HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/*" + bundleContext.registerService(Servlet.class, new HelloWorldServletWithinLimit(), new Hashtable<>(Map.of( + HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/withinlimit/*" + ))); + bundleContext.registerService(Servlet.class, new HelloWorldServletExceedingLimit(), new Hashtable<>(Map.of( + HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/exceedinglimit/*" ))); } @Test - public void testFormSizeLimit() throws Exception { + public void testRequestResponseLimits() throws Exception { HttpClientTransportOverHTTP transport = new HttpClientTransportOverHTTP(); HttpClient httpClient = new HttpClient(transport); httpClient.start(); @@ -101,31 +105,42 @@ public void testFormSizeLimit() throws Exception { Object value = bundleContext.getServiceReference(HttpService.class).getProperty("org.osgi.service.http.port"); int httpPort = Integer.parseInt((String) value); - URI uri = new URI(String.format("http://localhost:%d/endpoint", httpPort)); - Fields formFields = new Fields(); formFields.add(new Fields.Field("key", "value")); // under 10 bytes - ContentResponse response = httpClient.FORM(uri, formFields); + ContentResponse responseWithinLimit = httpClient.FORM(new URI(String.format("http://localhost:%d/withinlimit/a", httpPort)), formFields); + + // Request limit ok, response limit ok + assertEquals(200, responseWithinLimit.getStatus()); + assertEquals("OK", responseWithinLimit.getContentAsString()); - assertEquals(200, response.getStatus()); - assertEquals("OK", response.getContentAsString()); + // Request limit ok, response limit exceeded + // org.eclipse.jetty.http.HttpException$RuntimeException: 500: Response body is too large: 17>10 + ContentResponse responseExceedingLimit = httpClient.FORM(new URI(String.format("http://localhost:%d/exceedinglimit/a", httpPort)), formFields); + assertEquals(500, responseExceedingLimit.getStatus()); Fields formFieldsLimitExceeded = new Fields(); formFieldsLimitExceeded.add(new Fields.Field("key", "valueoverlimit")); // over limit of 10 bytes - ContentResponse responseExceeded = httpClient.FORM(uri, formFieldsLimitExceeded); + ContentResponse responseExceeded = httpClient.FORM(new URI(String.format("http://localhost:%d/withinlimit/a", httpPort)), formFieldsLimitExceeded); - // TODO why does this need yield a HTTP 413? - // Seems maxFormSize is not enforced? + // Request limit exceeded, HTTP 413 directly from Jetty assertEquals(413, responseExceeded.getStatus()); httpClient.close(); } - static final class HelloWorldServlet extends HttpServlet { + static final class HelloWorldServletWithinLimit extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setStatus(200); resp.getWriter().write("OK"); } } + + static final class HelloWorldServletExceedingLimit extends HttpServlet { + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + resp.setStatus(200); + resp.getWriter().write("responseoverlimit"); + } + } } From c07c58f65790f17e95df3969e5682a1da0ed7a45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20R=C3=BCtter?= Date: Wed, 7 May 2025 14:47:13 +0200 Subject: [PATCH 2/8] FELIX-6774 `org.apache.felix.http.jetty.maxFormSize` not enforced - Update README.md --- http/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/http/README.md b/http/README.md index d6b65bc8e7..cbe938488c 100644 --- a/http/README.md +++ b/http/README.md @@ -412,8 +412,8 @@ properties can be used (some legacy property names still exist but are not docum | `org.apache.felix.http.jetty.requestBufferSize` | Size of the buffer for requests not fitting the header buffer, in bytes. Default is 8 KB. | | `org.apache.felix.http.jetty.responseBufferSize` | Size of the buffer for responses, in bytes. Default is 24 KB. | | `org.apache.felix.http.jetty.maxFormSize` | The maximum size accepted for a form post, in bytes. Defaults to 200 KB. Deprecated in favor of `org.apache.felix.http.jetty.requestSizeLimit` since Jetty12 1.0.28. | -| `org.apache.felix.http.jetty.requestBufferSize` | Maximum size of the request body for submitted form/multipart content. Default is unlimited. Added in Jetty12 1.0.30 as replacement of `org.apache.felix.http.jetty.maxFormSize`. | -| `org.apache.felix.http.jetty.responseBufferSize` | Maximum size of a response. Default is unlimited. Default is unlimited. Added in Jetty12 1.0.30. | +| `org.apache.felix.http.jetty.requestSizeLimit` | Maximum size of the request body for submitted form/multipart content. Default is unlimited. Added in Jetty12 1.0.30 as replacement of `org.apache.felix.http.jetty.maxFormSize`. | +| `org.apache.felix.http.jetty.responseSizeLimit` | Maximum size of a response. Default is unlimited. Default is unlimited. Added in Jetty12 1.0.30. | | `org.apache.felix.http.mbeans` | If `true`, enables the MBean server functionality. The default is `false`. | | `org.apache.felix.http.jetty.sendServerHeader` | If `false`, the `Server` HTTP header is no longer included in responses. The default is `false`. | | `org.eclipse.jetty.servlet.SessionCookie` | Name of the cookie used to transport the Session ID. The default is `JSESSIONID`. | From 128784ebb69c288ac6277b4e18a60d7b594fee6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20R=C3=BCtter?= Date: Wed, 7 May 2025 20:15:42 +0200 Subject: [PATCH 3/8] FELIX-6776 Introduce way to restrict request and response size - introduce org.apache.felix.http.jetty.requestSizeLimit and org.apache.felix.http.jetty.responseSizeLimit - If either one is configured, the SizeLimitHandler will be configured, see https://jetty.org/docs/jetty/12/programming-guide/server/http.html#handler-use-size-limit - -1 is the default for both, which means unlimited. - Updated README.md for new properties --- http/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/http/README.md b/http/README.md index cbe938488c..f7a7a3cf6e 100644 --- a/http/README.md +++ b/http/README.md @@ -411,9 +411,9 @@ properties can be used (some legacy property names still exist but are not docum | `org.apache.felix.http.jetty.headerBufferSize` | Size of the buffer for request and response headers, in bytes. Default is 16 KB. | | `org.apache.felix.http.jetty.requestBufferSize` | Size of the buffer for requests not fitting the header buffer, in bytes. Default is 8 KB. | | `org.apache.felix.http.jetty.responseBufferSize` | Size of the buffer for responses, in bytes. Default is 24 KB. | -| `org.apache.felix.http.jetty.maxFormSize` | The maximum size accepted for a form post, in bytes. Defaults to 200 KB. Deprecated in favor of `org.apache.felix.http.jetty.requestSizeLimit` since Jetty12 1.0.28. | -| `org.apache.felix.http.jetty.requestSizeLimit` | Maximum size of the request body for submitted form/multipart content. Default is unlimited. Added in Jetty12 1.0.30 as replacement of `org.apache.felix.http.jetty.maxFormSize`. | -| `org.apache.felix.http.jetty.responseSizeLimit` | Maximum size of a response. Default is unlimited. Default is unlimited. Added in Jetty12 1.0.30. | +| `org.apache.felix.http.jetty.maxFormSize` | The maximum size accepted for a form post, in bytes (ony applies to form parameters). Defaults to 200 KB. | +| `org.apache.felix.http.jetty.requestSizeLimit` | Maximum size of the request body in bytes. Default is unlimited. Added in Jetty12 1.0.30. | +| `org.apache.felix.http.jetty.responseSizeLimit` | Maximum size of the response body in bytes. Default is unlimited. Default is unlimited. Added in Jetty12 1.0.30. | | `org.apache.felix.http.mbeans` | If `true`, enables the MBean server functionality. The default is `false`. | | `org.apache.felix.http.jetty.sendServerHeader` | If `false`, the `Server` HTTP header is no longer included in responses. The default is `false`. | | `org.eclipse.jetty.servlet.SessionCookie` | Name of the cookie used to transport the Session ID. The default is `JSESSIONID`. | From f6a84301f3f6fd355485e746590bf909bf3c9cbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20R=C3=BCtter?= Date: Wed, 7 May 2025 20:17:31 +0200 Subject: [PATCH 4/8] FELIX-6776 Introduce way to restrict request and response size - Remove deprecation message --- .../felix/http/jetty/internal/ConfigMetaTypeProvider.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/ConfigMetaTypeProvider.java b/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/ConfigMetaTypeProvider.java index c133eb2df5..3c6b13eff0 100644 --- a/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/ConfigMetaTypeProvider.java +++ b/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/ConfigMetaTypeProvider.java @@ -204,19 +204,19 @@ public ObjectClassDefinition getObjectClassDefinition( String id, String locale adList.add(new AttributeDefinitionImpl(JettyConfig.FELIX_JETTY_MAX_FORM_SIZE, "Maximum Form Size in bytes", - "Size of Body for submitted form content. Default is 200KB. Deprecated in favor of `org.apache.felix.http.jetty.requestSizeLimit`", + "Size of Body for submitted form content. Default is 200KB.", 204800, bundle.getBundleContext().getProperty(JettyConfig.FELIX_JETTY_MAX_FORM_SIZE))); adList.add(new AttributeDefinitionImpl(JettyConfig.FELIX_JETTY_REQUEST_SIZE_LIMIT, "Maximum request size in bytes", - "Maximum size of the request body for submitted form/multipart content. Default is unlimited.", + "Maximum size of the request body in bytes. Default is unlimited.", 204800, bundle.getBundleContext().getProperty(JettyConfig.FELIX_JETTY_REQUEST_SIZE_LIMIT))); adList.add(new AttributeDefinitionImpl(JettyConfig.FELIX_JETTY_RESPONSE_SIZE_LIMIT, "Maximum response size in bytes", - "Maximum size of a response. Default is unlimited.", + "Maximum size of the response body in bytes. Default is unlimited.", 204800, bundle.getBundleContext().getProperty(JettyConfig.FELIX_JETTY_RESPONSE_SIZE_LIMIT))); From 76464cfacd5be6b5f12f7e5535ad1905632bc9e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20R=C3=BCtter?= Date: Wed, 7 May 2025 20:18:18 +0200 Subject: [PATCH 5/8] FELIX-6776 Introduce way to restrict request and response size - Remove deprecation message --- .../java/org/apache/felix/http/jetty/internal/JettyConfig.java | 2 +- .../org/apache/felix/http/jetty/internal/JettyService.java | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java b/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java index 056cf0f1b4..c2961631b7 100644 --- a/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java +++ b/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java @@ -106,7 +106,7 @@ public final class JettyConfig /** Felix specific property to configure the request buffer size. Default is 24KB */ public static final String FELIX_JETTY_RESPONSE_BUFFER_SIZE = "org.apache.felix.http.jetty.responseBufferSize"; - /** Felix specific property to configure the max form size. Default is 200KB. This is deprecated in favor of `org.apache.felix.http.jetty.requestSizeLimit` */ + /** Felix specific property to configure the max form size. Default is 200KB. */ public static final String FELIX_JETTY_MAX_FORM_SIZE = "org.apache.felix.http.jetty.maxFormSize"; /** Felix specific property to configure the request size limit. Default is unlimited. See https://jetty.org/docs/jetty/12/programming-guide/server/http.html#handler-use-size-limit */ diff --git a/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/JettyService.java b/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/JettyService.java index 5e1a66c59d..047f4594ec 100644 --- a/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/JettyService.java +++ b/http/jetty12/src/main/java/org/apache/felix/http/jetty/internal/JettyService.java @@ -323,9 +323,6 @@ private void initializeJetty() throws Exception final ServletHolder holder = new ServletHolder(this.controller.createDispatcherServlet()); holder.setAsyncSupported(true); context.addServlet(holder, "/*"); - - // Not sure if this does anything still, since we're using async form API's - // see https://stackoverflow.com/a/78659566 context.setMaxFormContentSize(this.config.getMaxFormSize()); int requestSizeLimit = this.config.getRequestSizeLimit(); From acc937b79b1553cc56b6173dcad3f55badb61fc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20R=C3=BCtter?= Date: Mon, 12 May 2025 10:02:40 +0200 Subject: [PATCH 6/8] FELIX-6776-Introduce-way-to-restrict-request-and-response-size - Upgrade mockito to try and resolve the mockito issue on Java 23 - Upgrade test scope dependency of Jetty to 12.0.19 --- http/base/pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/http/base/pom.xml b/http/base/pom.xml index c4e9706856..423aed8886 100644 --- a/http/base/pom.xml +++ b/http/base/pom.xml @@ -144,7 +144,7 @@ 1.0.0 provided - + junit junit @@ -154,13 +154,13 @@ org.mockito mockito-core - 5.7.0 + 5.17.0 test org.eclipse.jetty.ee10.websocket jetty-ee10-websocket-jetty-server - 12.0.16 + 12.0.19 test From 9792c462d6ccbf2d38033e8b02d2a2c6ceece668 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20R=C3=BCtter?= Date: Mon, 12 May 2025 10:06:50 +0200 Subject: [PATCH 7/8] FELIX-6776-Introduce-way-to-restrict-request-and-response-size - Upgrade byte-buddy --- http/base/pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/http/base/pom.xml b/http/base/pom.xml index 423aed8886..eb6b5245c8 100644 --- a/http/base/pom.xml +++ b/http/base/pom.xml @@ -157,6 +157,12 @@ 5.17.0 test + + net.bytebuddy + byte-buddy + 1.17.5 + test + org.eclipse.jetty.ee10.websocket jetty-ee10-websocket-jetty-server From 9209c7439e4602926f64e68fa467d70aca1eaf61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20R=C3=BCtter?= Date: Mon, 12 May 2025 10:10:27 +0200 Subject: [PATCH 8/8] FELIX-6776-Introduce-way-to-restrict-request-and-response-size - Revert byte-buddy as it doesn't fix 23 --- http/base/pom.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/http/base/pom.xml b/http/base/pom.xml index eb6b5245c8..423aed8886 100644 --- a/http/base/pom.xml +++ b/http/base/pom.xml @@ -157,12 +157,6 @@ 5.17.0 test - - net.bytebuddy - byte-buddy - 1.17.5 - test - org.eclipse.jetty.ee10.websocket jetty-ee10-websocket-jetty-server