2222import io .undertow .UndertowMessages ;
2323import io .undertow .UndertowOptions ;
2424import io .undertow .server .handlers .Cookie ;
25+ import io .undertow .server .protocol .http .HttpRequestParser ;
26+ import io .undertow .util .BadRequestException ;
2527import io .undertow .util .DateUtils ;
2628import io .undertow .util .HeaderMap ;
2729import io .undertow .util .HeaderValues ;
@@ -446,7 +448,7 @@ public static void setExchangeRequestPath(final HttpServerExchange exchange, fin
446448 try {
447449 final boolean slashDecodingFlag = URLUtils .getSlashDecodingFlag (allowEncodedSlash , exchange .getConnection ().getUndertowOptions ().get (UndertowOptions .DECODE_SLASH ));
448450 setExchangeRequestPath (exchange , encodedPath , charset , decode , slashDecodingFlag , decodeBuffer , exchange .getConnection ().getUndertowOptions ().get (UndertowOptions .MAX_PARAMETERS , UndertowOptions .DEFAULT_MAX_PARAMETERS ));
449- } catch (ParameterLimitException e ) {
451+ } catch (ParameterLimitException | BadRequestException e ) {
450452 throw new RuntimeException (e );
451453 }
452454 }
@@ -459,8 +461,9 @@ public static void setExchangeRequestPath(final HttpServerExchange exchange, fin
459461 * @param encodedPath The encoded path to decode
460462 * @param decodeBuffer The decode buffer to use
461463 * @throws ParameterLimitException
464+ * @throws BadRequestException
462465 */
463- public static void setExchangeRequestPath (final HttpServerExchange exchange , final String encodedPath , StringBuilder decodeBuffer ) throws ParameterLimitException {
466+ public static void setExchangeRequestPath (final HttpServerExchange exchange , final String encodedPath , StringBuilder decodeBuffer ) throws ParameterLimitException , BadRequestException {
464467 final OptionMap options = exchange .getConnection ().getUndertowOptions ();
465468 boolean slashDecodingFlag = URLUtils .getSlashDecodingFlag (options );
466469 setExchangeRequestPath (exchange , encodedPath ,
@@ -474,16 +477,44 @@ public static void setExchangeRequestPath(final HttpServerExchange exchange, fin
474477 /**
475478 * Sets the request path and query parameters, decoding to the requested charset.
476479 *
477- * @param exchange The exchange
478- * @param encodedPath The encoded path
479- * @param charset The charset
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
488+ */
489+ 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
480506 */
481- public static void setExchangeRequestPath (final HttpServerExchange exchange , final String encodedPath , final String charset , boolean decode , final boolean decodeSlashFlag , StringBuilder decodeBuffer , int maxParameters ) throws ParameterLimitException {
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 {
508+ final OptionMap options = exchange .getConnection ().getUndertowOptions ();
509+ final boolean allowUnescapedCharactersInUrl = options .get (UndertowOptions .ALLOW_UNESCAPED_CHARACTERS_IN_URL , false );
482510 boolean requiresDecode = false ;
483511 final StringBuilder pathBuilder = new StringBuilder ();
484512 int currentPathPartIndex = 0 ;
485513 for (int i = 0 ; i < encodedPath .length (); ++i ) {
486514 char c = encodedPath .charAt (i );
515+ if (!allowUnescapedCharactersInUrl && !HttpRequestParser .isTargetCharacterAllowed (c )) {
516+ throw new BadRequestException (UndertowMessages .MESSAGES .invalidCharacterInRequestTarget (c ));
517+ }
487518 if (c == '?' ) {
488519 String part ;
489520 String encodedPart = encodedPath .substring (currentPathPartIndex , i );
@@ -496,10 +527,22 @@ public static void setExchangeRequestPath(final HttpServerExchange exchange, fin
496527 part = pathBuilder .toString ();
497528 exchange .setRequestPath (part );
498529 exchange .setRelativePath (part );
499- exchange .setRequestURI (encodedPath .substring (0 , i ));
530+ if (requiresDecode && allowUnescapedCharactersInUrl ) {
531+ final String uri = URLUtils .decode (encodedPath .substring (0 , i ), charset , decodeSlashFlag ,false , decodeBuffer );
532+ exchange .setRequestURI (uri );
533+ } else {
534+ exchange .setRequestURI (encodedPath .substring (0 , i ));
535+ }
536+
500537 final String qs = encodedPath .substring (i + 1 );
501- exchange .setQueryString (qs );
502- URLUtils .parseQueryString (qs , exchange , charset , decode , maxParameters );
538+ if (requiresDecode && allowUnescapedCharactersInUrl ) {
539+ final String decodedQS = URLUtils .decode (qs , charset , decodeSlashFlag ,false , decodeBuffer );
540+ exchange .setQueryString (decodedQS );
541+ } else {
542+ exchange .setQueryString (qs );
543+ }
544+
545+ URLUtils .parseQueryString (qs , exchange , charset , decodeQueryString , maxParameters );
503546 return ;
504547 } else if (c == ';' ) {
505548 String part ;
@@ -510,10 +553,16 @@ public static void setExchangeRequestPath(final HttpServerExchange exchange, fin
510553 part = encodedPart ;
511554 }
512555 pathBuilder .append (part );
513- exchange .setRequestURI (encodedPath );
556+ if (requiresDecode && allowUnescapedCharactersInUrl ) {
557+ final String uri = URLUtils .decode (encodedPath , charset , decodeSlashFlag ,false , decodeBuffer );
558+ exchange .setRequestURI (uri );
559+ } else {
560+ exchange .setRequestURI (encodedPath );
561+ }
562+
514563 currentPathPartIndex = i + 1 + URLUtils .parsePathParams (encodedPath .substring (i + 1 ), exchange , charset , decode , maxParameters );
515564 i = currentPathPartIndex -1 ;
516- } else if (c == '% ' || c == '+' ) {
565+ } else if (decode && ( c == '+ ' || c == '%' || c > 127 ) ) {
517566 requiresDecode = decode ;
518567 }
519568 }
0 commit comments