diff --git a/core/src/main/java/io/undertow/server/HttpServerExchange.java b/core/src/main/java/io/undertow/server/HttpServerExchange.java index 3515907dd1..d0c563453a 100644 --- a/core/src/main/java/io/undertow/server/HttpServerExchange.java +++ b/core/src/main/java/io/undertow/server/HttpServerExchange.java @@ -211,6 +211,10 @@ public final class HttpServerExchange extends AbstractAttachable { * the query string */ private String queryString = ""; + /** + * the non-decoded query string. Set only when query string goes through decoding + */ + private String nonDecodedQueryString = null; private int requestWrapperCount = 0; private ConduitWrapper[] requestWrappers; //we don't allocate these by default, as for get requests they are not used @@ -466,6 +470,7 @@ public String getRequestId() { * Examples: * GET http://localhost:8080/myFile.jsf?foo=bar HTTP/1.1 -> 'http://localhost:8080/myFile.jsf' * POST /my+File.jsf?foo=bar HTTP/1.1 -> '/my+File.jsf' + * For the query string, see {@link #getQueryString} and {@link #getNonDecodedQueryString} . */ public String getRequestURI() { return requestURI; @@ -589,10 +594,37 @@ public String getQueryString() { public HttpServerExchange setQueryString(final String queryString) { // Clean leading ? if( queryString.length() > 0 && queryString.charAt(0) == '?' ) { - this.queryString = queryString.substring(1); - } else { - this.queryString = queryString; - } + this.queryString = queryString.substring(1); + } else { + this.queryString = queryString; + } + return this; + } + + /** + * Returns the query string as originally contained in the request, without any decoding. + * The returned string does not contain the leading {@code '?'} char. + * + * @return The request query string, without the leading {@code '?'}, non-decoded. + */ + public String getNonDecodedQueryString() { + return this.nonDecodedQueryString == null? this.queryString: this.nonDecodedQueryString; + } + + /** + * Sets the non-decoded query string. Leading ? char will be removed automatically.

+ * Must be invoked only if the {@link #getQueryString() query string} has gone through decoding. In such case, we expect + * that both forms of the query string will be set in the exchange: {@link #setQueryString decoded} and non-decoded. + * + * @param nonDecodedQueryString the query string as originally contained in the request, without any decoding + */ + public HttpServerExchange setNonDecodedQueryString(String nonDecodedQueryString) { + // Clean leading ? + if( nonDecodedQueryString.length() > 0 && queryString.charAt(0) == '?' ) { + this.nonDecodedQueryString = nonDecodedQueryString.substring(1); + } else { + this.nonDecodedQueryString = nonDecodedQueryString; + } return this; } diff --git a/core/src/main/java/io/undertow/server/handlers/proxy/ProxyHandler.java b/core/src/main/java/io/undertow/server/handlers/proxy/ProxyHandler.java index 16ff1d27c0..a66baf2c65 100644 --- a/core/src/main/java/io/undertow/server/handlers/proxy/ProxyHandler.java +++ b/core/src/main/java/io/undertow/server/handlers/proxy/ProxyHandler.java @@ -449,7 +449,7 @@ public void run() { } requestURI.append(targetURI); - String qs = exchange.getQueryString(); + String qs = exchange.getUnencodedQueryString(); if (qs != null && !qs.isEmpty()) { requestURI.append('?'); requestURI.append(qs); diff --git a/core/src/main/java/io/undertow/server/protocol/http/HttpRequestParser.java b/core/src/main/java/io/undertow/server/protocol/http/HttpRequestParser.java index 6a6ff83e3e..da58c7efec 100644 --- a/core/src/main/java/io/undertow/server/protocol/http/HttpRequestParser.java +++ b/core/src/main/java/io/undertow/server/protocol/http/HttpRequestParser.java @@ -568,6 +568,7 @@ final void handleQueryParameters(ByteBuffer buffer, ParseState state, HttpServer if (next == ' ' || next == '\t') { String queryString = stringBuilder.toString(); if(urlDecodeRequired && this.allowUnescapedCharactersInUrl) { + exchange.setUnencodedQueryString(queryString); queryString = decode(queryString, urlDecodeRequired, state, slashDecodingFlag, false); } exchange.setQueryString(queryString);