Skip to content

Commit bf0cfcb

Browse files
committed
[UNDERTOW-2312] Fix ALLOW_UNESCAPED_CHARACTERS_IN_URL with query strings in Http2 upgrade
The new code caused ALLOW_UNESCAPED_CHARACTERS_IN_URL to not work correctly in HTTP2 upgrade scenarios when the characters are in the query. The reason for that is that Http2UpgradeHandler passes decode as false when asking Connectors to parse the request path, assuming that otherwise the decode would have been done twice. While that may the true for the parsing of the path, it is not the case when Connectors is parsing query params to set them one-by-one in the HttpServerExchange.This caused LotsOfQueryParametersTestCase to fail when run with HTTP2 Upgrade config. Signed-off-by: Flavia Rainone <frainone@redhat.com>
1 parent b95e66d commit bf0cfcb

File tree

3 files changed

+44
-13
lines changed

3 files changed

+44
-13
lines changed

core/src/main/java/io/undertow/server/Connectors.java

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -477,12 +477,34 @@ public static void setExchangeRequestPath(final HttpServerExchange exchange, fin
477477
/**
478478
* Sets the request path and query parameters, decoding to the requested charset.
479479
*
480-
* @param exchange The exchange
481-
* @param encodedPath The encoded path
482-
* @param charset The charset
483-
* @throws BadRequestException
480+
* @param exchange the exchange
481+
* @param encodedPath the encoded path
482+
* @param decode indicates if the request path should be decoded
483+
* @param decodeSlashFlag indicates if slash characters contained in the encoded path should be decoded
484+
* @param decodeBuffer the buffer used for decoding
485+
* @param maxParameters maximum number of parameters allowed in the path
486+
* @param charset the charset
487+
* @throws BadRequestException if there is something wrong with the request, such as non-allowed characters
484488
*/
485489
public static void setExchangeRequestPath(final HttpServerExchange exchange, final String encodedPath, final String charset, boolean decode, final boolean decodeSlashFlag, StringBuilder decodeBuffer, int maxParameters) throws ParameterLimitException, BadRequestException {
490+
setExchangeRequestPath(exchange, encodedPath, charset, decode, decode, decodeSlashFlag, decodeBuffer, maxParameters);
491+
}
492+
493+
/**
494+
* Sets the request path and query parameters, decoding to the requested charset.
495+
*
496+
* @param exchange the exchange
497+
* @param encodedPath the encoded path
498+
* @param decode indicates if the request path should be decoded, apart from the query string part of the
499+
* request (see next parameter)
500+
* @param decodeQueryString indicates if the query string of the path, when present, should be decoded
501+
* @param decodeSlashFlag indicates if slash characters contained in the request path should be decoded
502+
* @param decodeBuffer the buffer used for decoding
503+
* @param maxParameters maximum number of parameters allowed in the path
504+
* @param charset the charset
505+
* @throws BadRequestException if there is something wrong with the request, such as non-allowed characters
506+
*/
507+
public static void setExchangeRequestPath(final HttpServerExchange exchange, final String encodedPath, final String charset, boolean decode, boolean decodeQueryString, final boolean decodeSlashFlag, StringBuilder decodeBuffer, int maxParameters) throws ParameterLimitException, BadRequestException {
486508
final OptionMap options = exchange.getConnection().getUndertowOptions();
487509
final boolean allowUnescapedCharactersInUrl = options.get(UndertowOptions.ALLOW_UNESCAPED_CHARACTERS_IN_URL, false);
488510
boolean requiresDecode = false;
@@ -520,7 +542,7 @@ public static void setExchangeRequestPath(final HttpServerExchange exchange, fin
520542
exchange.setQueryString(qs);
521543
}
522544

523-
URLUtils.parseQueryString(qs, exchange, charset, decode, maxParameters);
545+
URLUtils.parseQueryString(qs, exchange, charset, decodeQueryString, maxParameters);
524546
return;
525547
} else if(c == ';') {
526548
String part;

core/src/main/java/io/undertow/server/protocol/http2/Http2ReceiveListener.java

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -218,15 +218,19 @@ public void handleEvent(Http2StreamSourceChannel channel) {
218218
* @param initial The initial upgrade request that started the HTTP2 connection
219219
*/
220220
void handleInitialRequest(HttpServerExchange initial, Http2Channel channel, byte[] data) {
221-
handleInitialRequest(initial, channel, data, this.decode);
221+
handleInitialRequest(initial, channel, data, this.decode, this.decode);
222222
}
223223

224224
/**
225-
* Handles the initial request when the exchange was started by a HTTP upgrade.
226-
*
227-
* @param initial The initial upgrade request that started the HTTP2 connection
228-
*/
229-
void handleInitialRequest(HttpServerExchange initial, Http2Channel channel, byte[] data, boolean decode) {
225+
* Handles the initial request when the exchange was started by a HTTP upgrade.
226+
*
227+
* @param initial the initial upgrade request that started the HTTP2 connection
228+
* @param channel the channel that received the request
229+
* @param data any extra data read by the channel that has not been parsed yet
230+
* @param decode indicates if the request path should be decoded, apart from the query string
231+
* @param decodeQueryString indicates if the query string of the path, when present, should be decoded
232+
*/
233+
void handleInitialRequest(HttpServerExchange initial, Http2Channel channel, byte[] data, boolean decode, boolean decodeQueryString) {
230234
//we have a request
231235
Http2HeadersStreamSinkChannel sink = channel.createInitialUpgradeResponseStream();
232236
final Http2ServerConnection connection = new Http2ServerConnection(channel, sink, undertowOptions, bufferSize, rootHandler);
@@ -253,7 +257,7 @@ void handleInitialRequest(HttpServerExchange initial, Http2Channel channel, byte
253257
Connectors.terminateRequest(exchange);
254258
String uri = exchange.getQueryString().isEmpty() ? initial.getRequestURI() : initial.getRequestURI() + '?' + exchange.getQueryString();
255259
try {
256-
Connectors.setExchangeRequestPath(exchange, uri, encoding, decode, slashDecodingFlag, decodeBuffer, maxParameters);
260+
Connectors.setExchangeRequestPath(exchange, uri, encoding, decode, decodeQueryString, slashDecodingFlag, decodeBuffer, maxParameters);
257261
} catch (ParameterLimitException | BadRequestException e) {
258262
exchange.setStatusCode(StatusCodes.BAD_REQUEST);
259263
exchange.endExchange();

core/src/main/java/io/undertow/server/protocol/http2/Http2UpgradeHandler.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,12 @@ public void handleRequest(HttpServerExchange exchange) throws Exception {
176176
}, undertowOptions, exchange.getConnection().getBufferSize(), null);
177177
channel.getReceiveSetter().set(receiveListener);
178178
// don't decode requests from upgrade, they are already decoded by the parser for protocol HTTP 1.1 (HttpRequestParser)
179-
receiveListener.handleInitialRequest(exchange, channel, data, false);
179+
// however, the queries have to be decoded, since this is decoded only once, when the connector parses the query string
180+
// to fill in the query param collection of the HttpServerExchange (see Connectors invoking URLUtils.QUERY_STRING_PARSER)
181+
final boolean decodeURL = undertowOptions.get(UndertowOptions.DECODE_URL, true);
182+
final boolean allowUnescapedCharactersInURL = undertowOptions.get(UndertowOptions.ALLOW_UNESCAPED_CHARACTERS_IN_URL, false);
183+
// if allowUnescapedCharactersInURL is true, the decoding has already been done
184+
receiveListener.handleInitialRequest(exchange, channel, data, decodeURL && !allowUnescapedCharactersInURL, decodeURL);
180185
channel.resumeReceives();
181186
}
182187
});

0 commit comments

Comments
 (0)