diff --git a/core/src/main/java/io/undertow/server/Connectors.java b/core/src/main/java/io/undertow/server/Connectors.java index afb6e68d5e..6634edf98e 100644 --- a/core/src/main/java/io/undertow/server/Connectors.java +++ b/core/src/main/java/io/undertow/server/Connectors.java @@ -505,7 +505,7 @@ public static void setExchangeRequestPath(final HttpServerExchange exchange, fin part = pathBuilder.toString(); exchange.setRequestPath(part); exchange.setRelativePath(part); - if(requiresDecode && allowUnescapedCharactersInUrl) { + if(requiresDecode && allowUnescapedCharactersInUrl && exchange.isUpgrade()) { final String uri = URLUtils.decode(encodedPath.substring(0, i), charset, decodeSlashFlag,false, decodeBuffer); exchange.setRequestURI(uri); } else { diff --git a/core/src/main/java/io/undertow/server/protocol/ajp/AjpRequestParser.java b/core/src/main/java/io/undertow/server/protocol/ajp/AjpRequestParser.java index 14c3c215f6..9e47c8e9ec 100644 --- a/core/src/main/java/io/undertow/server/protocol/ajp/AjpRequestParser.java +++ b/core/src/main/java/io/undertow/server/protocol/ajp/AjpRequestParser.java @@ -269,11 +269,13 @@ public void parse(final ByteBuffer buf, final AjpRequestParseState state, final int colon = result.value.indexOf(';'); if (colon == -1) { String res = decode(result.value, result.containsUrlCharacters); - if(result.containsUnencodedCharacters) { + if(result.containsUnencodedCharacters || result.containsUrlCharacters) { //we decode if the URL was non-compliant, and contained incorrectly encoded characters //there is not really a 'correct' thing to do in this situation, but this seems the least incorrect exchange.setRequestURI(res); - } else { + } /*else if (allowUnescapedCharactersInUrl) { + + } */else { exchange.setRequestURI(result.value); } exchange.setRequestPath(res); @@ -297,7 +299,7 @@ public void parse(final ByteBuffer buf, final AjpRequestParseState state, final resBuilder.append(decode(url, result.containsUrlCharacters)); } final String res = resBuilder.toString(); - if(result.containsUnencodedCharacters) { + if(result.containsUnencodedCharacters || result.containsUrlCharacters || allowUnescapedCharactersInUrl) { exchange.setRequestURI(res); } else { exchange.setRequestURI(result.value); @@ -447,7 +449,7 @@ public void parse(final ByteBuffer buf, final AjpRequestParseState state, final state.state = AjpRequestParseState.READING_ATTRIBUTES; return; } - if(resultHolder.containsUnencodedCharacters) { + if(resultHolder.containsUnencodedCharacters || (resultHolder.containsUrlCharacters && allowUnescapedCharactersInUrl)) { result = decode(resultHolder.value, true); decodingAlreadyDone = true; } else { @@ -583,8 +585,8 @@ protected StringHolder parseString(ByteBuffer buf, AjpRequestParseState state, S return new StringHolder(null, false, false, false); } byte c = buf.get(); - if(type == StringType.QUERY_STRING && (c == '+' || c == '%' || c < 0 )) { - if (c < 0) { + if(type == StringType.QUERY_STRING && (c == '+' || c == '%' || c < 0 || c > 127 )) { + if (c < 0 || c > 127) { if (!allowUnescapedCharactersInUrl) { throw new BadRequestException(); } else { @@ -592,7 +594,7 @@ protected StringHolder parseString(ByteBuffer buf, AjpRequestParseState state, S } } containsUrlCharacters = true; - } else if(type == StringType.URL && (c == '%' || c < 0 )) { + } else if(type == StringType.URL && (c == '%' || c < 0 || c > 127 )) { if(c < 0 ) { if(!allowUnescapedCharactersInUrl) { throw new BadRequestException(); diff --git a/core/src/main/java/io/undertow/server/protocol/http2/Http2ReceiveListener.java b/core/src/main/java/io/undertow/server/protocol/http2/Http2ReceiveListener.java index 02ff95ebe7..29db6d8611 100644 --- a/core/src/main/java/io/undertow/server/protocol/http2/Http2ReceiveListener.java +++ b/core/src/main/java/io/undertow/server/protocol/http2/Http2ReceiveListener.java @@ -218,6 +218,15 @@ public void handleEvent(Http2StreamSourceChannel channel) { * @param initial The initial upgrade request that started the HTTP2 connection */ void handleInitialRequest(HttpServerExchange initial, Http2Channel channel, byte[] data) { + handleInitialRequest(initial, channel, data, this.decode); + } + + /** + * Handles the initial request when the exchange was started by a HTTP upgrade. + * + * @param initial The initial upgrade request that started the HTTP2 connection + */ + void handleInitialRequest(HttpServerExchange initial, Http2Channel channel, byte[] data, boolean decode) { //we have a request Http2HeadersStreamSinkChannel sink = channel.createInitialUpgradeResponseStream(); final Http2ServerConnection connection = new Http2ServerConnection(channel, sink, undertowOptions, bufferSize, rootHandler); diff --git a/core/src/main/java/io/undertow/server/protocol/http2/Http2UpgradeHandler.java b/core/src/main/java/io/undertow/server/protocol/http2/Http2UpgradeHandler.java index 2a1ca672cc..f338ea940e 100644 --- a/core/src/main/java/io/undertow/server/protocol/http2/Http2UpgradeHandler.java +++ b/core/src/main/java/io/undertow/server/protocol/http2/Http2UpgradeHandler.java @@ -175,7 +175,8 @@ public void handleRequest(HttpServerExchange exchange) throws Exception { } }, undertowOptions, exchange.getConnection().getBufferSize(), null); channel.getReceiveSetter().set(receiveListener); - receiveListener.handleInitialRequest(exchange, channel, data); + // don't decode requests from upgrade, they are already decoded by the parser for protocol HTTP 1.1 (HttpRequestParser) + receiveListener.handleInitialRequest(exchange, channel, data, false); channel.resumeReceives(); } }); diff --git a/core/src/test/java/io/undertow/server/handlers/accesslog/AccessLogFileWithUnescapedCharactersTestCase.java b/core/src/test/java/io/undertow/server/handlers/accesslog/AccessLogFileWithUnescapedCharactersTestCase.java index 50b2343e20..9c131a7bfb 100644 --- a/core/src/test/java/io/undertow/server/handlers/accesslog/AccessLogFileWithUnescapedCharactersTestCase.java +++ b/core/src/test/java/io/undertow/server/handlers/accesslog/AccessLogFileWithUnescapedCharactersTestCase.java @@ -94,6 +94,7 @@ private void verifySingleLogMessageToFile(Path logFileName, DefaultAccessLogRece //old = DefaultServer.getUndertowOptions(); DefaultServer.setUndertowOptions( OptionMap.create(UndertowOptions.ALLOW_UNESCAPED_CHARACTERS_IN_URL, true)); + DefaultServer.setServerOptions(OptionMap.create(UndertowOptions.ALLOW_UNESCAPED_CHARACTERS_IN_URL, true)); TestHttpClient client = new TestHttpClient(); try { HttpGet get = new HttpGet(DefaultServer.getDefaultServerURL() + "/helloworld/한글이름_test.html?param=한글이름_ahoy"); @@ -107,7 +108,8 @@ private void verifySingleLogMessageToFile(Path logFileName, DefaultAccessLogRece logReceiver.awaitWrittenForTest(); String written = new String(Files.readAllBytes(logFileName)); System.out.println("Look, this is written:\n" + written); - Assert.assertEquals(DefaultServer.getDefaultServerAddress().getAddress().getHostAddress() + " \"GET " + "/helloworld/한글이름_test.html?param=한글이름_ahoy HTTP/1.1\" 200 5" + System.lineSeparator(), written); + final String protocolVersion = DefaultServer.isH2()? "HTTP/2.0" : result.getProtocolVersion().toString(); + Assert.assertEquals(DefaultServer.getDefaultServerAddress().getAddress().getHostAddress() + " \"GET " + "/helloworld/한글이름_test.html?param=한글이름_ahoy " + protocolVersion + "\" 200 5" + System.lineSeparator(), written); } finally { client.getConnectionManager().shutdown(); }