Index: core/src/main/java/io/undertow/UndertowMessages.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
<+>/*\n * JBoss, Home of Professional Open Source.\n * Copyright 2014 Red Hat, Inc., and individual contributors\n * as indicated by the @author tags.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage io.undertow;\n\nimport java.io.IOException;\nimport java.net.SocketAddress;\nimport java.nio.channels.ClosedChannelException;\n\nimport io.undertow.predicate.PredicateBuilder;\nimport io.undertow.protocols.http2.HpackException;\nimport io.undertow.server.handlers.builder.HandlerBuilder;\nimport org.jboss.logging.Messages;\nimport org.jboss.logging.annotations.Cause;\nimport org.jboss.logging.annotations.Message;\nimport org.jboss.logging.annotations.MessageBundle;\n\nimport javax.net.ssl.SSLPeerUnverifiedException;\n\n/**\n * @author Stuart Douglas\n */\n@MessageBundle(projectCode = \"UT\")\npublic interface UndertowMessages {\n\n    UndertowMessages MESSAGES = Messages.getBundle(UndertowMessages.class);\n\n    @Message(id = 1, value = \"Maximum concurrent requests must be larger than zero.\")\n    IllegalArgumentException maximumConcurrentRequestsMustBeLargerThanZero();\n\n    @Message(id = 2, value = \"The response has already been started\")\n    IllegalStateException responseAlreadyStarted();\n\n    // id = 3\n\n    @Message(id = 4, value = \"getResponseChannel() has already been called\")\n    IllegalStateException responseChannelAlreadyProvided();\n\n    @Message(id = 5, value = \"getRequestChannel() has already been called\")\n    IllegalStateException requestChannelAlreadyProvided();\n\n    // id = 6\n\n    // id = 7\n\n    @Message(id = 8, value = \"Handler cannot be null\")\n    IllegalArgumentException handlerCannotBeNull();\n\n    @Message(id = 9, value = \"Path must be specified\")\n    IllegalArgumentException pathMustBeSpecified();\n\n    @Message(id = 10, value = \"Session not found %s\")\n    IllegalStateException sessionNotFound(final String session);\n\n    @Message(id = 11, value = \"Session manager must not be null\")\n    IllegalStateException sessionManagerMustNotBeNull();\n\n    @Message(id = 12, value = \"Session manager was not attached to the request. Make sure that the SessionAttachmentHandler is installed in the handler chain\")\n    IllegalStateException sessionManagerNotFound();\n\n    @Message(id = 13, value = \"Argument %s cannot be null\")\n    IllegalArgumentException argumentCannotBeNull(final String argument);\n\n    @Message(id = 14, value = \"close() called with data still to be flushed. Please call shutdownWrites() and then call flush() until it returns true before calling close()\")\n    IOException closeCalledWithDataStillToBeFlushed();\n\n    @Message(id = 16, value = \"Could not add cookie as cookie handler was not present in the handler chain\")\n    IllegalStateException cookieHandlerNotPresent();\n\n    @Message(id = 17, value = \"Form value is a file, use getFile() instead\")\n    IllegalStateException formValueIsAFile();\n\n    @Message(id = 18, value = \"Form value is a String, use getValue() instead\")\n    IllegalStateException formValueIsAString();\n\n    @Message(id = 19, value = \"Connection from %s terminated as request entity was larger than %s\")\n    IOException requestEntityWasTooLarge(SocketAddress address, long size);\n\n    @Message(id = 20, value = \"Connection terminated as request was larger than %s\")\n    IOException requestEntityWasTooLarge(long size);\n\n    @Message(id = 21, value = \"Session already invalidated\")\n    IllegalStateException sessionAlreadyInvalidated();\n\n    @Message(id = 22, value = \"The specified hash algorithm '%s' can not be found.\")\n    IllegalArgumentException hashAlgorithmNotFound(String algorithmName);\n\n    @Message(id = 23, value = \"An invalid Base64 token has been received.\")\n    IllegalArgumentException invalidBase64Token(@Cause final IOException cause);\n\n    @Message(id = 24, value = \"An invalidly formatted nonce has been received.\")\n    IllegalArgumentException invalidNonceReceived();\n\n    @Message(id = 25, value = \"Unexpected token '%s' within header.\")\n    IllegalArgumentException unexpectedTokenInHeader(final String name);\n\n    @Message(id = 26, value = \"Invalid header received.\")\n    IllegalArgumentException invalidHeader();\n\n    @Message(id = 27, value = \"Could not find session cookie config in the request\")\n    IllegalStateException couldNotFindSessionCookieConfig();\n\n    @Message(id = 28, value = \"Session %s already exists\")\n    IllegalStateException sessionAlreadyExists(final String id);\n\n    @Message(id = 29, value = \"Channel was closed mid chunk, if you have attempted to write chunked data you cannot shutdown the channel until after it has all been written.\")\n    IOException chunkedChannelClosedMidChunk();\n\n    @Message(id = 30, value = \"User %s successfully authenticated.\")\n    String userAuthenticated(final String userName);\n\n    @Message(id = 31, value = \"User %s has logged out.\")\n    String userLoggedOut(final String userName);\n\n    @Message(id = 33, value = \"Authentication type %s cannot be combined with %s\")\n    IllegalStateException authTypeCannotBeCombined(String type, String existing);\n\n    @Message(id = 34, value = \"Stream is closed\")\n    IOException streamIsClosed();\n\n    @Message(id = 35, value = \"Cannot get stream as startBlocking has not been invoked\")\n    IllegalStateException startBlockingHasNotBeenCalled();\n\n    @Message(id = 36, value = \"Connection terminated parsing multipart data\")\n    IOException connectionTerminatedReadingMultiPartData();\n\n    @Message(id = 37, value = \"Failed to parse path in HTTP request\")\n    RuntimeException failedToParsePath();\n\n    @Message(id = 38, value = \"Authentication failed, requested user name '%s'\")\n    String authenticationFailed(final String userName);\n\n    @Message(id = 39, value = \"To many query parameters, cannot have more than %s query parameters\")\n    RuntimeException tooManyQueryParameters(int noParams);\n\n    @Message(id = 40, value = \"To many headers, cannot have more than %s header\")\n    RuntimeException tooManyHeaders(int noParams);\n\n    @Message(id = 41, value = \"Channel is closed\")\n    ClosedChannelException channelIsClosed();\n\n    @Message(id = 42, value = \"Could not decode trailers in HTTP request\")\n    IOException couldNotDecodeTrailers();\n\n    @Message(id = 43, value = \"Data is already being sent. You must wait for the completion callback to be be invoked before calling send() again\")\n    IllegalStateException dataAlreadyQueued();\n\n    @Message(id = 44, value = \"More than one predicate with name %s. Builder class %s and %s\")\n    IllegalStateException moreThanOnePredicateWithName(String name, Class<? extends PredicateBuilder> aClass, Class<? extends PredicateBuilder> existing);\n\n    @Message(id = 45, value = \"Error parsing predicate string %s:%n%s\")\n    IllegalArgumentException errorParsingPredicateString(String reason, String s);\n\n    @Message(id = 46, value = \"The number of cookies sent exceeded the maximum of %s\")\n    IllegalStateException tooManyCookies(int maxCookies);\n\n    @Message(id = 47, value = \"The number of parameters exceeded the maximum of %s\")\n    IllegalStateException tooManyParameters(int maxValues);\n\n    @Message(id = 48, value = \"No request is currently active\")\n    IllegalStateException noRequestActive();\n\n    @Message(id = 50, value = \"AuthenticationMechanism Outcome is null\")\n    IllegalStateException authMechanismOutcomeNull();\n\n    @Message(id = 51, value = \"Not a valid IP pattern %s\")\n    IllegalArgumentException notAValidIpPattern(String peer);\n\n    @Message(id = 52, value = \"Session data requested when non session based authentication in use\")\n    IllegalStateException noSessionData();\n\n    @Message(id = 53, value = \"Listener %s already registered\")\n    IllegalArgumentException listenerAlreadyRegistered(String name);\n\n    @Message(id = 54, value = \"The maximum size %s for an individual file in a multipart request was exceeded\")\n    IOException maxFileSizeExceeded(long maxIndividualFileSize);\n\n    @Message(id = 55, value = \"Could not set attribute %s to %s as it is read only\")\n    String couldNotSetAttribute(String attributeName, String newValue);\n\n    @Message(id = 56, value = \"Could not parse URI template %s, exception at char %s\")\n    RuntimeException couldNotParseUriTemplate(String path, int i);\n\n    @Message(id = 57, value = \"Mismatched braces in attribute string %s\")\n    RuntimeException mismatchedBraces(String valueString);\n\n    @Message(id = 58, value = \"More than one handler with name %s. Builder class %s and %s\")\n    IllegalStateException moreThanOneHandlerWithName(String name, Class<? extends HandlerBuilder> aClass, Class<? extends HandlerBuilder> existing);\n\n    @Message(id = 59, value = \"Invalid syntax %s\")\n    IllegalArgumentException invalidSyntax(String line);\n\n    @Message(id = 60, value = \"Error parsing handler string %s:%n%s\")\n    IllegalArgumentException errorParsingHandlerString(String reason, String s);\n\n    @Message(id = 61, value = \"Out of band responses only allowed for 100-continue requests\")\n    IllegalArgumentException outOfBandResponseOnlyAllowedFor100Continue();\n\n    @Message(id = 62, value = \"AJP does not support HTTP upgrade\")\n    IllegalStateException ajpDoesNotSupportHTTPUpgrade();\n\n    @Message(id = 63, value = \"File system watcher already started\")\n    IllegalStateException fileSystemWatcherAlreadyStarted();\n\n    @Message(id = 64, value = \"File system watcher not started\")\n    IllegalStateException fileSystemWatcherNotStarted();\n\n    @Message(id = 65, value = \"SSL must be specified to connect to a https URL\")\n    IOException sslWasNull();\n\n    @Message(id = 66, value = \"Incorrect magic number %s for AJP packet header\")\n    IOException wrongMagicNumber(int number);\n\n    @Message(id = 67, value = \"No client cert was provided\")\n    SSLPeerUnverifiedException peerUnverified();\n\n    @Message(id = 68, value = \"Servlet path match failed\")\n    IllegalArgumentException servletPathMatchFailed();\n\n    @Message(id = 69, value = \"Could not parse set cookie header %s\")\n    IllegalArgumentException couldNotParseCookie(String headerValue);\n\n    @Message(id = 70, value = \"method can only be called by IO thread\")\n    IllegalStateException canOnlyBeCalledByIoThread();\n\n    @Message(id = 71, value = \"Cannot add path template %s, matcher already contains an equivalent pattern %s\")\n    IllegalStateException matcherAlreadyContainsTemplate(String templateString, String templateString1);\n\n    @Message(id = 72, value = \"Failed to decode url %s to charset %s\")\n    IllegalArgumentException failedToDecodeURL(String s, String enc, @Cause Exception e);\n\n\n    @Message(id = 73, value = \"Resource change listeners are not supported\")\n    IllegalArgumentException resourceChangeListenerNotSupported();\n\n    @Message(id = 74, value = \"Could not renegotiate SSL connection to require client certificate, as client had sent more data\")\n    IllegalStateException couldNotRenegotiate();\n\n    @Message(id = 75, value = \"Object was freed\")\n    IllegalStateException objectWasFreed();\n\n    @Message(id = 76, value = \"Handler not shutdown\")\n    IllegalStateException handlerNotShutdown();\n\n    @Message(id = 77, value = \"The underlying transport does not support HTTP upgrade\")\n    IllegalStateException upgradeNotSupported();\n\n    @Message(id = 78, value = \"Renegotiation not supported\")\n    IOException renegotiationNotSupported();\n\n    @Message(id = 79, value = \"Not a valid user agent pattern %s\")\n    IllegalArgumentException notAValidUserAgentPattern(String userAgent);\n\n    @Message(id = 80, value = \"Not a valid regular expression pattern %s\")\n    IllegalArgumentException notAValidRegularExpressionPattern(String pattern);\n\n    @Message(id = 81, value = \"Bad request\")\n    RuntimeException badRequest();\n\n    @Message(id = 82, value = \"Host %s already registered\")\n    RuntimeException hostAlreadyRegistered(Object host);\n\n    @Message(id = 83, value = \"Host %s has not been registered\")\n    RuntimeException hostHasNotBeenRegistered(Object host);\n\n    @Message(id = 84, value = \"Attempted to write additional data after the last chunk\")\n    IOException extraDataWrittenAfterChunkEnd();\n\n    @Message(id = 85, value = \"Could not generate unique session id\")\n    RuntimeException couldNotGenerateUniqueSessionId();\n\n    @Message(id = 86, value = \"SPDY needs to be provided with a heap buffer pool, for use in compressing and decompressing headers.\")\n    IllegalArgumentException mustProvideHeapBuffer();\n\n    @Message(id = 87, value = \"Unexpected SPDY frame type %s\")\n    IOException unexpectedFrameType(int type);\n\n    @Message(id = 88, value = \"SPDY control frames cannot have body content\")\n    IOException controlFrameCannotHaveBodyContent();\n\n    @Message(id = 89, value = \"SPDY not supported\")\n    IOException spdyNotSupported();\n\n    @Message(id = 90, value = \"Jetty NPN not available\")\n    IOException jettyNPNNotAvailable();\n\n    @Message(id = 91, value = \"Buffer has already been freed\")\n    IllegalStateException bufferAlreadyFreed();\n\n    @Message(id = 92, value = \"A SPDY header was too large to fit in a response buffer, if you want to support larger headers please increase the buffer size\")\n    IllegalStateException headersTooLargeToFitInHeapBuffer();\n\n    @Message(id = 93, value = \"A SPDY stream was reset by the remote endpoint\")\n    IOException spdyStreamWasReset();\n\n    @Message(id = 94, value = \"Blocking await method called from IO thread. Blocking IO must be dispatched to a worker thread or deadlocks will result.\")\n    IOException awaitCalledFromIoThread();\n\n    @Message(id = 95, value = \"Recursive call to flushSenders()\")\n    RuntimeException recursiveCallToFlushingSenders();\n\n    @Message(id = 96, value = \"More data was written to the channel than specified in the content-length\")\n    IllegalStateException fixedLengthOverflow();\n\n    @Message(id = 97, value = \"AJP request already in progress\")\n    IllegalStateException ajpRequestAlreadyInProgress();\n\n    @Message(id = 98, value = \"HTTP ping data must be 8 bytes in length\")\n    String httpPingDataMustBeLength8();\n\n    @Message(id = 99, value = \"Received a ping of size other than 8\")\n    String invalidPingSize();\n\n    @Message(id = 100, value = \"stream id must be zero for frame type %s\")\n    String streamIdMustBeZeroForFrameType(int frameType);\n\n    @Message(id = 101, value = \"stream id must not be zero for frame type %s\")\n    String streamIdMustNotBeZeroForFrameType(int frameType);\n\n    @Message(id = 102, value = \"RST_STREAM received for idle stream\")\n    String rstStreamReceivedForIdleStream();\n\n    @Message(id = 103, value = \"Http2 stream was reset\")\n    IOException http2StreamWasReset();\n\n    @Message(id = 104, value = \"Incorrect HTTP2 preface\")\n    IOException incorrectHttp2Preface();\n\n    @Message(id = 105, value = \"HTTP2 frame to large\")\n    IOException http2FrameTooLarge();\n\n    @Message(id = 106, value = \"HTTP2 continuation frame received without a corresponding headers or push promise frame\")\n    IOException http2ContinuationFrameNotExpected();\n\n    @Message(id = 107, value = \"Huffman encoded value in HPACK headers did not end with EOS padding\")\n    HpackException huffmanEncodedHpackValueDidNotEndWithEOS();\n\n    @Message(id = 108, value = \"HPACK variable length integer encoded over too many octects, max is %s\")\n    HpackException integerEncodedOverTooManyOctets(int maxIntegerOctets);\n\n    @Message(id = 109, value = \"Zero is not a valid header table index\")\n    HpackException zeroNotValidHeaderTableIndex();\n\n\n    @Message(id = 110, value = \"Cannot send 100-Continue, getResponseChannel() has already been called\")\n    IOException cannotSendContinueResponse();\n\n    @Message(id = 111, value = \"Parser did not make progress\")\n    IOException parserDidNotMakeProgress();\n\n    @Message(id = 112, value = \"Only client side can call createStream, if you wish to send a PUSH_PROMISE frame use createPushPromiseStream instead\")\n    IOException headersStreamCanOnlyBeCreatedByClient();\n\n    @Message(id = 113, value = \"Only the server side can send a push promise stream\")\n    IOException pushPromiseCanOnlyBeCreatedByServer();\n\n    @Message(id = 114, value = \"Invalid IP access control rule %s. Format is: [ip-match] allow|deny\")\n    IllegalArgumentException invalidAclRule(String rule);\n\n    @Message(id = 115, value = \"Server received PUSH_PROMISE frame from client\")\n    IOException serverReceivedPushPromise();\n\n    @Message(id = 116, value = \"CONNECT not supported by this connector\")\n    IllegalStateException connectNotSupported();\n\n    @Message(id = 117, value = \"Request was not a CONNECT request\")\n    IllegalStateException notAConnectRequest();\n\n    @Message(id = 118, value = \"Cannot reset buffer, response has already been commited\")\n    IllegalStateException cannotResetBuffer();\n\n    @Message(id = 119, value = \"HTTP2 via prior knowledge failed\")\n    IOException http2PriRequestFailed();\n\n    @Message(id = 120, value = \"Out of band responses are not allowed for this connector\")\n    IllegalStateException outOfBandResponseNotSupported();\n\n    @Message(id = 121, value = \"Session was rejected as the maximum number of sessions (%s) has been hit\")\n    IllegalStateException tooManySessions(int maxSessions);\n\n    @Message(id = 122, value = \"CONNECT attempt failed as target proxy returned %s\")\n    IOException proxyConnectionFailed(int responseCode);\n\n    @Message(id = 123, value = \"MCMP message %s rejected due to suspicious characters\")\n    RuntimeException mcmpMessageRejectedDueToSuspiciousCharacters(String data);\n\n    @Message(id = 128, value = \"Remote peer closed connection before all data could be read\")\n    IOException couldNotReadContentLengthData();\n\n}\n
===================================================================
--- core/src/main/java/io/undertow/UndertowMessages.java	(revision af09eaf414c8a2cae790fbeacdf703e448b07be6)
+++ core/src/main/java/io/undertow/UndertowMessages.java	(revision )
@@ -402,4 +402,8 @@
     @Message(id = 128, value = "Remote peer closed connection before all data could be read")
     IOException couldNotReadContentLengthData();
 
+    @Message(id = 133, value = "The size of parameters exceeded the maximum size of %s bytes")
+    IllegalStateException parametersSizeExceeded(int maxSize);
+
+
 }
Index: core/src/test/java/io/undertow/server/handlers/form/FormDataSizeCheckTestCase.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- core/src/test/java/io/undertow/server/handlers/form/FormDataSizeCheckTestCase.java	(revision )
+++ core/src/test/java/io/undertow/server/handlers/form/FormDataSizeCheckTestCase.java	(revision )
@@ -0,0 +1,218 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2014 Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags.
+ *
+ * Licensed 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 io.undertow.server.handlers.form;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.http.Consts;
+import org.apache.http.HttpResponse;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.utils.URLEncodedUtils;
+import org.apache.http.entity.AbstractHttpEntity;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.entity.ContentType;
+import org.apache.http.message.BasicNameValuePair;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.xnio.OptionMap;
+
+import io.undertow.UndertowOptions;
+import io.undertow.server.HttpHandler;
+import io.undertow.server.HttpServerExchange;
+import io.undertow.server.handlers.BlockingHandler;
+import io.undertow.testutils.DefaultServer;
+import io.undertow.testutils.HttpClientUtils;
+import io.undertow.testutils.TestHttpClient;
+import io.undertow.util.Headers;
+import io.undertow.util.HttpString;
+import io.undertow.util.StatusCodes;
+import junit.textui.TestRunner;
+
+/**
+ * @author Stuart Douglas
+ */
+@RunWith(DefaultServer.Parameterized.class)
+public class FormDataSizeCheckTestCase {
+
+    private static OptionMap undertowOptions;
+
+    static class AggregateRunner extends TestRunner {
+
+    }
+
+    private final HttpHandler rootHandler;
+
+    public FormDataSizeCheckTestCase(final HttpHandler rootHandler) {
+        this.rootHandler = rootHandler;
+    }
+
+    @BeforeClass
+    public static void setUndertowOption() {
+        undertowOptions = DefaultServer.getUndertowOptions();
+        DefaultServer.setUndertowOptions(OptionMap.builder().set(UndertowOptions.MAX_PARAMETERS_SIZE, 20).addAll(undertowOptions).getMap());
+    }
+
+    @AfterClass
+    public static void resetUndertowOption() {
+        DefaultServer.setUndertowOptions(OptionMap.builder().addAll(undertowOptions).getMap());
+    }
+
+    @Parameterized.Parameters
+    public static Collection<Object[]> handlerChains() {
+        List<Object[]> ret = new ArrayList<>();
+        final FormParserFactory parserFactory = FormParserFactory.builder().build();
+        HttpHandler fd = new HttpHandler() {
+            @Override
+            public void handleRequest(final HttpServerExchange exchange) throws Exception {
+                final FormDataParser parser = parserFactory.createParser(exchange);
+                parser.parse(new HttpHandler() {
+                    @Override
+                    public void handleRequest(final HttpServerExchange exchange) throws Exception {
+                        if (exchange.getResponseCode() < 300 ) {
+                            FormData data = exchange.getAttachment(FormDataParser.FORM_DATA);
+                            Iterator<String> it = data.iterator();
+                            while (it.hasNext()) {
+                                String fd = it.next();
+                                for (FormData.FormValue val : data.get(fd)) {
+                                    exchange.getResponseHeaders().add(new HttpString(fd), val.getValue());
+                                }
+                            }
+                        }
+                    }
+                });
+
+            }
+        };
+        ret.add(new Object[]{fd});
+        final BlockingHandler blocking = new BlockingHandler();
+
+        blocking.setRootHandler(new HttpHandler() {
+
+
+            @Override
+            public void handleRequest(final HttpServerExchange exchange) throws Exception {
+                final FormDataParser parser = parserFactory.createParser(exchange);
+                try {
+                    FormData data = parser.parseBlocking();
+                    Iterator<String> it = data.iterator();
+                    while (it.hasNext()) {
+                        String fd = it.next();
+                        for (FormData.FormValue val : data.get(fd)) {
+                            exchange.getResponseHeaders().add(new HttpString(fd), val.getValue());
+                        }
+                    }
+                } catch (IOException e) {
+                    exchange.setResponseCode(StatusCodes.INTERNAL_SERVER_ERROR);
+                }
+            }
+        });
+        ret.add(new Object[]{blocking});
+        return ret;
+
+    }
+
+
+    @Test
+    public void testFormParametersSizeBiggest() throws Exception {
+        final int statusCode = runTestForSize(veryBigSize());
+        //Assert.assertEquals(StatusCodes.REQUEST_ENTITY_TOO_LARGE, statusCode);
+        Assert.assertEquals(StatusCodes.INTERNAL_SERVER_ERROR, statusCode);
+
+    }
+
+
+    @Test
+    public void testFormParametersSizeBigger() throws Exception {
+        final int statusCode = runTestForSize(testFormValidSize(21));
+        //Assert.assertEquals(StatusCodes.REQUEST_ENTITY_TOO_LARGE, statusCode);
+        Assert.assertEquals(StatusCodes.INTERNAL_SERVER_ERROR, statusCode);
+    }
+
+
+    @Test
+    public void testFormParametersSizeSmaller() throws Exception {
+        final int statusCode = runTestForSize(testFormValidSize(19));
+        Assert.assertEquals(StatusCodes.OK, statusCode);
+
+    }
+
+
+    @Test
+    public void testFormParametersExactSize() throws Exception {
+        final int statusCode = runTestForSize(testFormValidSize(20));
+        Assert.assertEquals(StatusCodes.OK, statusCode);
+    }
+
+    private AbstractHttpEntity testFormValidSize(int size) throws Exception {
+
+        final String name = URLEncodedUtils.format(Arrays.asList(
+                        new BasicNameValuePair("nò#ame", "A Value"),
+                        new BasicNameValuePair("Single-value", null),
+                        new BasicNameValuePair("A/name/with_special*chars", "A $ value&& with=SomeCharacters&")
+                ),
+                Consts.UTF_8.name()
+        );
+
+        return new ByteArrayEntity(Arrays.copyOfRange(name.getBytes(Consts.UTF_8), 0, size), ContentType.create(URLEncodedUtils.CONTENT_TYPE, Consts.UTF_8.name()));
+
+    }
+
+    private AbstractHttpEntity veryBigSize() throws Exception {
+
+        final byte[] bytes = new byte[1024 * 1024 * 4];
+        Arrays.fill(bytes, (byte)'a');
+        return new ByteArrayEntity(bytes);
+
+    }
+
+    private int runTestForSize(AbstractHttpEntity entity) throws Exception {
+        DefaultServer.setRootHandler(rootHandler);
+
+        TestHttpClient client = new TestHttpClient();
+        try {
+
+            HttpPost post = new HttpPost(DefaultServer.getDefaultServerURL() + "/path");
+            post.setHeader(Headers.CONTENT_TYPE_STRING, FormEncodedDataDefinition.APPLICATION_X_WWW_FORM_URLENCODED);
+            post.setEntity(entity);
+            HttpResponse result = client.execute(post);
+            HttpClientUtils.readResponse(result);
+            return result.getStatusLine().getStatusCode();
+
+        } finally {
+            client.getConnectionManager().shutdown();
+        }
+    }
+
+    private void checkResult(final List<NameValuePair> data, final HttpResponse result) {
+        for (NameValuePair vp : data) {
+            Assert.assertEquals(vp.getValue() == null ? "" : vp.getValue(), result.getHeaders(vp.getName())[0].getValue());
+        }
+    }
+
+}
Index: core/src/main/java/io/undertow/server/handlers/form/FormEncodedDataDefinition.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
<+>/*\n * JBoss, Home of Professional Open Source.\n * Copyright 2014 Red Hat, Inc., and individual contributors\n * as indicated by the @author tags.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage io.undertow.server.handlers.form;\n\nimport java.io.IOException;\nimport java.net.URLDecoder;\nimport java.nio.ByteBuffer;\n\nimport io.undertow.UndertowLogger;\nimport io.undertow.UndertowMessages;\nimport io.undertow.UndertowOptions;\nimport io.undertow.server.HttpHandler;\nimport io.undertow.server.HttpServerExchange;\nimport io.undertow.util.Headers;\nimport io.undertow.util.SameThreadExecutor;\nimport org.xnio.ChannelListener;\nimport org.xnio.IoUtils;\nimport org.xnio.Pooled;\nimport org.xnio.channels.StreamSourceChannel;\n\n/**\n * Parser definition for form encoded data. This handler takes effect for any request that has a mime type\n * of application/x-www-form-urlencoded. The handler attaches a {@link FormDataParser} to the chain\n * that can parse the underlying form data asynchronously.\n *\n * @author Stuart Douglas\n */\npublic class FormEncodedDataDefinition implements FormParserFactory.ParserDefinition<FormEncodedDataDefinition> {\n\n    public static final String APPLICATION_X_WWW_FORM_URLENCODED = \"application/x-www-form-urlencoded\";\n    private String defaultEncoding = \"ISO-8859-1\";\n    private boolean forceCreation = false; //if the parser should be created even if the correct headers are missing\n\n    public FormEncodedDataDefinition() {\n    }\n\n    @Override\n    public FormDataParser create(final HttpServerExchange exchange)  {\n        String mimeType = exchange.getRequestHeaders().getFirst(Headers.CONTENT_TYPE);\n        if (forceCreation || (mimeType != null && mimeType.startsWith(APPLICATION_X_WWW_FORM_URLENCODED))) {\n\n            String charset = defaultEncoding;\n            String contentType = exchange.getRequestHeaders().getFirst(Headers.CONTENT_TYPE);\n            if (contentType != null) {\n                String cs = Headers.extractQuotedValueFromHeader(contentType, \"charset\");\n                if (cs != null) {\n                    charset = cs;\n                }\n            }\n            return new FormEncodedDataParser(charset, exchange);\n        }\n        return null;\n    }\n\n    public String getDefaultEncoding() {\n        return defaultEncoding;\n    }\n\n    public boolean isForceCreation() {\n        return forceCreation;\n    }\n\n    public FormEncodedDataDefinition setForceCreation(boolean forceCreation) {\n        this.forceCreation = forceCreation;\n        return this;\n    }\n\n    public FormEncodedDataDefinition setDefaultEncoding(final String defaultEncoding) {\n        this.defaultEncoding = defaultEncoding;\n        return this;\n    }\n\n    private static final class FormEncodedDataParser implements ChannelListener<StreamSourceChannel>, FormDataParser {\n\n        private final HttpServerExchange exchange;\n        private final FormData data;\n        private final StringBuilder builder = new StringBuilder();\n        private String name = null;\n        private String charset;\n        private HttpHandler handler;\n\n        //0= parsing name\n        //1=parsing name, decode required\n        //2=parsing value\n        //3=parsing value, decode required\n        //4=finished\n        private int state = 0;\n\n        private FormEncodedDataParser(final String charset, final HttpServerExchange exchange) {\n            this.exchange = exchange;\n            this.charset = charset;\n            this.data = new FormData(exchange.getConnection().getUndertowOptions().get(UndertowOptions.MAX_PARAMETERS, 1000));\n        }\n\n        @Override\n        public void handleEvent(final StreamSourceChannel channel) {\n            try {\n                doParse(channel);\n                if (state == 4) {\n                    exchange.dispatch(SameThreadExecutor.INSTANCE, handler);\n                }\n            } catch (IOException e) {\n                IoUtils.safeClose(channel);\n                UndertowLogger.REQUEST_LOGGER.ioExceptionReadingFromChannel(e);\n                exchange.endExchange();\n\n            }\n        }\n\n        private void doParse(final StreamSourceChannel channel) throws IOException {\n            int c = 0;\n            final Pooled<ByteBuffer> pooled = exchange.getConnection().getBufferPool().allocate();\n            try {\n                final ByteBuffer buffer = pooled.getResource();\n                do {\n                    buffer.clear();\n                    c = channel.read(buffer);\n                    if (c > 0) {\n                        buffer.flip();\n                        while (buffer.hasRemaining()) {\n                            byte n = buffer.get();\n                            switch (state) {\n                                case 0: {\n                                    if (n == '=') {\n                                        name = builder.toString();\n                                        builder.setLength(0);\n                                        state = 2;\n                                    } else if (n == '&') {\n                                        data.add(builder.toString(), \"\");\n                                        builder.setLength(0);\n                                        state = 0;\n                                    } else if (n == '%' || n == '+') {\n                                        state = 1;\n                                        builder.append((char) n);\n                                    } else {\n                                        builder.append((char) n);\n                                    }\n                                    break;\n                                }\n                                case 1: {\n                                    if (n == '=') {\n                                        name = URLDecoder.decode(builder.toString(), charset);\n                                        builder.setLength(0);\n                                        state = 2;\n                                    } else if (n == '&') {\n                                        data.add(URLDecoder.decode(builder.toString(), charset), \"\");\n                                        builder.setLength(0);\n                                        state = 0;\n                                    } else {\n                                        builder.append((char) n);\n                                    }\n                                    break;\n                                }\n                                case 2: {\n                                    if (n == '&') {\n                                        data.add(name, builder.toString());\n                                        builder.setLength(0);\n                                        state = 0;\n                                    } else if (n == '%' || n == '+') {\n                                        state = 3;\n                                        builder.append((char) n);\n                                    } else {\n                                        builder.append((char) n);\n                                    }\n                                    break;\n                                }\n                                case 3: {\n                                    if (n == '&') {\n                                        data.add(name, URLDecoder.decode(builder.toString(), charset));\n                                        builder.setLength(0);\n                                        state = 0;\n                                    } else {\n                                        builder.append((char) n);\n                                    }\n                                    break;\n                                }\n                            }\n                        }\n                    }\n                } while (c > 0);\n                if (c == -1) {\n                    if (state == 2) {\n                        data.add(name, builder.toString());\n                    } else if (state == 3) {\n                        data.add(name, URLDecoder.decode(builder.toString(), charset));\n                    } else if(builder.length() > 0) {\n                        if(state == 1) {\n                            data.add(URLDecoder.decode(builder.toString(), charset), \"\");\n                        } else {\n                            data.add(builder.toString(), \"\");\n                        }\n                    }\n                    state = 4;\n                    exchange.putAttachment(FORM_DATA, data);\n                }\n            } finally {\n                pooled.free();\n            }\n        }\n\n\n        @Override\n        public void parse(HttpHandler handler) throws Exception {\n            if (exchange.getAttachment(FORM_DATA) != null) {\n                handler.handleRequest(exchange);\n                return;\n            }\n            this.handler = handler;\n            StreamSourceChannel channel = exchange.getRequestChannel();\n            if (channel == null) {\n                throw new IOException(UndertowMessages.MESSAGES.requestChannelAlreadyProvided());\n            } else {\n                doParse(channel);\n                if (state != 4) {\n                    channel.getReadSetter().set(this);\n                    channel.resumeReads();\n                } else {\n                    exchange.dispatch(SameThreadExecutor.INSTANCE, handler);\n                }\n            }\n        }\n\n        @Override\n        public FormData parseBlocking() throws IOException {\n            final FormData existing = exchange.getAttachment(FORM_DATA);\n            if (existing != null) {\n                return existing;\n            }\n\n            StreamSourceChannel channel = exchange.getRequestChannel();\n            if (channel == null) {\n                throw new IOException(UndertowMessages.MESSAGES.requestChannelAlreadyProvided());\n            } else {\n                while (state != 4) {\n                    doParse(channel);\n                    if (state != 4) {\n                        channel.awaitReadable();\n                    }\n                }\n            }\n            return data;\n        }\n\n        @Override\n        public void close() throws IOException {\n\n        }\n\n        @Override\n        public void setCharacterEncoding(final String encoding) {\n            this.charset = encoding;\n        }\n    }\n\n}\n
===================================================================
--- core/src/main/java/io/undertow/server/handlers/form/FormEncodedDataDefinition.java	(revision af09eaf414c8a2cae790fbeacdf703e448b07be6)
+++ core/src/main/java/io/undertow/server/handlers/form/FormEncodedDataDefinition.java	(revision )
@@ -91,6 +91,7 @@
         private final HttpServerExchange exchange;
         private final FormData data;
         private final StringBuilder builder = new StringBuilder();
+        private final int maxParametersSize;
         private String name = null;
         private String charset;
         private HttpHandler handler;
@@ -105,6 +106,7 @@
         private FormEncodedDataParser(final String charset, final HttpServerExchange exchange) {
             this.exchange = exchange;
             this.charset = charset;
+            this.maxParametersSize = exchange.getConnection().getUndertowOptions().get(UndertowOptions.MAX_PARAMETERS_SIZE, 4*1024*1024);
             this.data = new FormData(exchange.getConnection().getUndertowOptions().get(UndertowOptions.MAX_PARAMETERS, 1000));
         }
 
@@ -125,6 +127,7 @@
 
         private void doParse(final StreamSourceChannel channel) throws IOException {
             int c = 0;
+            long total = 0;
             final Pooled<ByteBuffer> pooled = exchange.getConnection().getBufferPool().allocate();
             try {
                 final ByteBuffer buffer = pooled.getResource();
@@ -132,6 +135,10 @@
                     buffer.clear();
                     c = channel.read(buffer);
                     if (c > 0) {
+                        total += c;
+                        if (total > maxParametersSize && maxParametersSize != -1) {
+                            throw UndertowMessages.MESSAGES.parametersSizeExceeded(maxParametersSize);
+                        }
                         buffer.flip();
                         while (buffer.hasRemaining()) {
                             byte n = buffer.get();
Index: core/src/main/java/io/undertow/UndertowOptions.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
<+>/*\n * JBoss, Home of Professional Open Source.\n * Copyright 2014 Red Hat, Inc., and individual contributors\n * as indicated by the @author tags.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\npackage io.undertow;\n\nimport org.xnio.Option;\n\n/**\n * @author Stuart Douglas\n */\npublic class UndertowOptions {\n\n    /**\n     * The maximum size in bytes of a http request header.\n     */\n    public static final Option<Integer> MAX_HEADER_SIZE = Option.simple(UndertowOptions.class, \"MAX_HEADER_SIZE\", Integer.class);\n    /**\n     * The default size we allow for the HTTP header.\n     */\n    public static final int DEFAULT_MAX_HEADER_SIZE = 1024 * 1024;\n\n    /**\n     * The default maximum size of the HTTP entity body.\n     */\n    public static final Option<Long> MAX_ENTITY_SIZE = Option.simple(UndertowOptions.class, \"MAX_ENTITY_SIZE\", Long.class);\n\n    /**\n     * We do not have a default upload limit\n     */\n    public static final long DEFAULT_MAX_ENTITY_SIZE = -1;\n\n    /**\n     * If we should buffer pipelined requests. Defaults to false.\n     */\n    public static final Option<Boolean> BUFFER_PIPELINED_DATA = Option.simple(UndertowOptions.class, \"BUFFER_PIPELINED_DATA\", Boolean.class);\n\n    /**\n     * The idle timeout in milliseconds after which the channel will be closed.\n     *\n     * If the underlying channel already has a read or write timeout set the smaller of the two values will be used\n     * for read/write timeouts.\n     *\n     */\n    public static final Option<Integer> IDLE_TIMEOUT = Option.simple(UndertowOptions.class, \"IDLE_TIMEOUT\", Integer.class);\n\n    /**\n     * The maximum allowed time of reading HTTP request in milliseconds.\n     *\n     * <code>-1</code> or missing value disables this functionality.\n     */\n    public static final Option<Integer> REQUEST_PARSE_TIMEOUT = Option.simple(UndertowOptions.class, \"REQUEST_PARSE_TIMEOUT\", Integer.class);\n\n    /**\n     * The amount of time the connection can be idle with no current requests before it is closed;\n     */\n    public static final Option<Integer> NO_REQUEST_TIMEOUT = Option.simple(UndertowOptions.class, \"NO_REQUEST_TIMEOUT\", Integer.class);\n\n    /**\n     * The maximum number of parameters that will be parsed. This is used to protect against hash vulnerabilities.\n     * <p>\n     * This applies to both query parameters, and to POST data, but is not cumulative (i.e. you can potentially have\n     * max parameters * 2 total parameters).\n     * <p>\n     * Defaults to 1000\n     */\n    public static final Option<Integer> MAX_PARAMETERS = Option.simple(UndertowOptions.class, \"MAX_PARAMETERS\", Integer.class);\n\n    /**\n     * The maximum number of headers that will be parsed. This is used to protect against hash vulnerabilities.\n     * <p>\n     * Defaults to 200\n     */\n    public static final Option<Integer> MAX_HEADERS = Option.simple(UndertowOptions.class, \"MAX_HEADERS\", Integer.class);\n\n\n    /**\n     * The maximum number of cookies that will be parsed. This is used to protect against hash vulnerabilities.\n     * <p>\n     * Defaults to 200\n     */\n    public static final Option<Integer> MAX_COOKIES = Option.simple(UndertowOptions.class, \"MAX_COOKIES\", Integer.class);\n\n    /**\n     * If a request comes in with encoded / characters (i.e. %2F), will these be decoded.\n     * <p>\n     * This can cause security problems if a front end proxy does not perform the same decoding, and as a result\n     * this is disabled by default.\n     * <p>\n     * Defaults to false\n     *\n     * See <a href=\"http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2007-0450\">CVE-2007-0450</a>\n     */\n    public static final Option<Boolean> ALLOW_ENCODED_SLASH = Option.simple(UndertowOptions.class, \"ALLOW_ENCODED_SLASH\", Boolean.class);\n\n    /**\n     * If this is true then the parser will decode the URL and query parameters using the selected character encoding (UTF-8 by default). If this is false they will\n     * not be decoded. This will allow a later handler to decode them into whatever charset is desired.\n     * <p>\n     * Defaults to true.\n     */\n    public static final Option<Boolean> DECODE_URL = Option.simple(UndertowOptions.class, \"DECODE_URL\", Boolean.class);\n\n\n    /**\n     * If this is true then the parser will decode the URL and query parameters using the selected character encoding (UTF-8 by default). If this is false they will\n     * not be decoded. This will allow a later handler to decode them into whatever charset is desired.\n     * <p>\n     * Defaults to true.\n     */\n    public static final Option<String> URL_CHARSET = Option.simple(UndertowOptions.class, \"URL_CHARSET\", String.class);\n\n    /**\n     * If this is true then a Connection: keep-alive header will be added to responses, even when it is not strictly required by\n     * the specification.\n     * <p>\n     * Defaults to true\n     */\n    public static final Option<Boolean> ALWAYS_SET_KEEP_ALIVE = Option.simple(UndertowOptions.class, \"ALWAYS_SET_KEEP_ALIVE\", Boolean.class);\n\n    /**\n     * If this is true then a Date header will be added to all responses. The HTTP spec says this header should be added to all\n     * responses, unless the server does not have an accurate clock.\n     * <p>\n     * Defaults to true\n     */\n    public static final Option<Boolean> ALWAYS_SET_DATE = Option.simple(UndertowOptions.class, \"ALWAYS_SET_DATE\", Boolean.class);\n\n    /**\n     * Maximum size of a buffered request, in bytes\n     * <p>\n     * Requests are not usually buffered, the most common case is when performing SSL renegotiation for a POST request, and the post data must be fully\n     * buffered in order to perform the renegotiation.\n     * <p>\n     * Defaults to 16384.\n     */\n    public static final Option<Integer> MAX_BUFFERED_REQUEST_SIZE = Option.simple(UndertowOptions.class, \"MAX_BUFFERED_REQUEST_SIZE\", Integer.class);\n\n    /**\n     * If this is true then Undertow will record the request start time, to allow for request time to be logged\n     *\n     * This has a small but measurable performance impact\n     *\n     * default is false\n     */\n    public static final Option<Boolean> RECORD_REQUEST_START_TIME = Option.simple(UndertowOptions.class, \"RECORD_REQUEST_START_TIME\", Boolean.class);\n\n    /**\n     * If this is true then Undertow will allow non-escaped equals characters in unquoted cookie values.\n     * <p>\n     * Unquoted cookie values may not contain equals characters. If present the value ends before the equals sign. The remainder of the cookie value will be dropped.\n     * <p>\n     * default is false\n     */\n    public static final Option<Boolean> ALLOW_EQUALS_IN_COOKIE_VALUE = Option.simple(UndertowOptions.class, \"ALLOW_EQUALS_IN_COOKIE_VALUE\", Boolean.class);\n\n    /**\n     * If we should attempt to use SPDY for HTTPS connections.\n     */\n    public static final Option<Boolean> ENABLE_SPDY = Option.simple(UndertowOptions.class, \"ENABLE_SPDY\", Boolean.class);\n\n    /**\n     * If we should attempt to use HTTP2 for HTTPS connections.\n     */\n    public static final Option<Boolean> ENABLE_HTTP2 = Option.simple(UndertowOptions.class, \"ENABLE_HTTP2\", Boolean.class);\n\n    /**\n     * If connector level statistics should be enabled. This has a slight performance impact, but allows statistics such\n     * as bytes sent/recevied to be monitored.\n     */\n    public static final Option<Boolean> ENABLE_CONNECTOR_STATISTICS = Option.simple(UndertowOptions.class, \"ENABLE_CONNECTOR_STATISTICS\", Boolean.class);\n\n    /**\n     * If unknown protocols should be allowed. The known protocols are:\n     *\n     * HTTP/0.9\n     * HTTP/1.0\n     * HTTP/1.1\n     * HTTP/2.0\n     *\n     * If this is false then requests that specify any other protocol will be rejected with a 400\n     *\n     * Defaults to false\n     */\n    public static final Option<Boolean> ALLOW_UNKNOWN_PROTOCOLS = Option.simple(UndertowOptions.class, \"ALLOW_UNKNOWN_PROTOCOLS\", Boolean.class);\n\n    /**\n     * The size of the header table that is used in the encoder\n     */\n    public static final Option<Integer> HTTP2_SETTINGS_HEADER_TABLE_SIZE = Option.simple(UndertowOptions.class, \"HTTP2_SETTINGS_HEADER_TABLE_SIZE\", Integer.class);\n    public static final int HTTP2_SETTINGS_HEADER_TABLE_SIZE_DEFAULT = 4096;\n\n    /**\n     * If push should be enabled for this connection.\n     */\n    public static final Option<Boolean> HTTP2_SETTINGS_ENABLE_PUSH = Option.simple(UndertowOptions.class, \"HTTP2_SETTINGS_ENABLE_PUSH\", Boolean.class);\n\n    /**\n     * The maximum number of concurrent\n     */\n    public static final Option<Integer> HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS = Option.simple(UndertowOptions.class, \"HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS\", Integer.class);\n\n    public static final Option<Integer> HTTP2_SETTINGS_INITIAL_WINDOW_SIZE = Option.simple(UndertowOptions.class, \"HTTP2_SETTINGS_INITIAL_WINDOW_SIZE\", Integer.class);\n    public static final Option<Integer> HTTP2_SETTINGS_MAX_FRAME_SIZE = Option.simple(UndertowOptions.class, \"HTTP2_SETTINGS_MAX_FRAME_SIZE\", Integer.class);\n    public static final Option<Integer> HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = Option.simple(UndertowOptions.class, \"HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE\", Integer.class);\n\n    /**\n     * Undertow keeps a LRU cache of common huffman encodings. This sets the maximum size, setting this to 0 will disable the caching.\n     *\n     */\n    public static final Option<Integer> HTTP2_HUFFMAN_CACHE_SIZE = Option.simple(UndertowOptions.class, \"HTTP2_HUFFMAN_CACHE_SIZE\", Integer.class);\n\n    /**\n     * The maximum number of concurrent requests that will be processed at a time. This differs from max concurrent streams in that it is not sent to the remote client.\n     *\n     * If the number of pending requests exceeds this number then requests will be queued, the difference between this and max concurrent streams determins\n     * the maximum number of requests that will be queued.\n     *\n     * Queued requests are processed by a priority queue, rather than a FIFO based queue, using HTTP2 stream priority.\n     *\n     * If this number is smaller than or equal to zero then max concurrent streams determins the maximum number of streams that can be run.\n     *\n     *\n     */\n    public static final Option<Integer> MAX_CONCURRENT_REQUESTS_PER_CONNECTION = Option.simple(UndertowOptions.class, \"MAX_CONCURRENT_REQUESTS_PER_CONNECTION\", Integer.class);\n\n    private UndertowOptions() {\n\n    }\n}\n
===================================================================
--- core/src/main/java/io/undertow/UndertowOptions.java	(revision af09eaf414c8a2cae790fbeacdf703e448b07be6)
+++ core/src/main/java/io/undertow/UndertowOptions.java	(revision )
@@ -71,6 +71,15 @@
     public static final Option<Integer> NO_REQUEST_TIMEOUT = Option.simple(UndertowOptions.class, "NO_REQUEST_TIMEOUT", Integer.class);
 
     /**
+     * The maximum number of the form data that will be parsed as parameters.
+     * <p>
+     * This applies only to POST data.
+     * <p>
+     * Defaults to 2MB
+     */
+    public static final Option<Integer> MAX_PARAMETERS_SIZE = Option.simple(UndertowOptions.class, "MAX_PARAMETERS_SIZE", Integer.class);
+
+    /**
      * The maximum number of parameters that will be parsed. This is used to protect against hash vulnerabilities.
      * <p>
      * This applies to both query parameters, and to POST data, but is not cumulative (i.e. you can potentially have
