Skip to content
Commits on Source (3)
......@@ -25,12 +25,12 @@
<parent>
<groupId>io.undertow</groupId>
<artifactId>undertow-parent</artifactId>
<version>2.0.25.Final</version>
<version>2.0.26.Final</version>
</parent>
<groupId>io.undertow</groupId>
<artifactId>undertow-benchmarks</artifactId>
<version>2.0.25.Final</version>
<version>2.0.26.Final</version>
<name>Undertow Benchmarks</name>
......
......@@ -25,12 +25,12 @@
<parent>
<groupId>io.undertow</groupId>
<artifactId>undertow-parent</artifactId>
<version>2.0.25.Final</version>
<version>2.0.26.Final</version>
</parent>
<groupId>io.undertow</groupId>
<artifactId>undertow-core</artifactId>
<version>2.0.25.Final</version>
<version>2.0.26.Final</version>
<name>Undertow Core</name>
......
......@@ -27,6 +27,7 @@ import javax.net.ssl.SSLPeerUnverifiedException;
import io.undertow.server.RequestTooBigException;
import io.undertow.server.handlers.form.MultiPartParserDefinition;
import io.undertow.util.UrlDecodeException;
import org.jboss.logging.Messages;
import org.jboss.logging.annotations.Cause;
import org.jboss.logging.annotations.Message;
......@@ -249,7 +250,7 @@ public interface UndertowMessages {
IllegalStateException matcherAlreadyContainsTemplate(String templateString, String templateString1);
@Message(id = 72, value = "Failed to decode url %s to charset %s")
IllegalArgumentException failedToDecodeURL(String s, String enc, @Cause Exception e);
UrlDecodeException failedToDecodeURL(String s, String enc, @Cause Exception e);
@Message(id = 73, value = "Resource change listeners are not supported")
......@@ -597,4 +598,10 @@ public interface UndertowMessages {
@Message(id = 192, value = "Form value is a in-memory file, use getFileItem() instead")
IllegalStateException formValueIsInMemoryFile();
@Message(id = 193, value = "Character decoding failed. Parameter [%s] with value [%s] has been ignored. Note: further occurrences of Parameter errors will be logged at DEBUG level.")
String failedToDecodeParameterValue(String parameter, String value, @Cause Exception e);
@Message(id = 194, value = "Character decoding failed. Parameter with name [%s] has been ignored. Note: further occurrences of Parameter errors will be logged at DEBUG level.")
String failedToDecodeParameterName(String parameter, @Cause Exception e);
}
......@@ -267,7 +267,7 @@ public class HpackDecoder {
if (index <= Hpack.STATIC_TABLE_LENGTH) {
return Hpack.STATIC_TABLE[index].name;
} else {
if (index >= Hpack.STATIC_TABLE_LENGTH + filledTableSlots) {
if (index > Hpack.STATIC_TABLE_LENGTH + filledTableSlots) {
throw new HpackException();
}
int adjustedIndex = getRealIndex(index - Hpack.STATIC_TABLE_LENGTH);
......
......@@ -1083,6 +1083,7 @@ public class SslConduit implements StreamSourceConduit, StreamSinkConduit {
try {
doHandshake();
} catch (IOException | RuntimeException | Error e) {
UndertowLogger.REQUEST_LOGGER.error("Closing SSLConduit after exception on handshake", e);
IoUtils.safeClose(connection);
}
if (anyAreSet(state, FLAG_READS_RESUMED)) {
......
......@@ -37,6 +37,8 @@ public interface AuthenticationMechanismFactory {
String LOGIN_PAGE = "login_page";
String ERROR_PAGE = "error_page";
String CONTEXT_PATH = "context_path";
String DEFAULT_PAGE = "default_page";
String OVERRIDE_INITIAL = "override_initial";
/**
* Creates an authentication mechanism using the specified properties
......
......@@ -125,7 +125,7 @@ public class RoutingHandler implements HttpHandler {
if (res == null) {
matcher.add(template, res = new RoutingMatch());
}
if (allMethodsMatcher.get(template) == null) {
if (allMethodsMatcher.match(template) == null) {
allMethodsMatcher.add(template, res);
}
res.defaultHandler = handler;
......@@ -161,7 +161,7 @@ public class RoutingHandler implements HttpHandler {
if (res == null) {
matcher.add(template, res = new RoutingMatch());
}
if (allMethodsMatcher.get(template) == null) {
if (allMethodsMatcher.match(template) == null) {
allMethodsMatcher.add(template, res);
}
res.predicatedHandlers.add(new HandlerHolder(predicate, handler));
......@@ -195,7 +195,7 @@ public class RoutingHandler implements HttpHandler {
// If we use allMethodsMatcher.addAll() we can have duplicate
// PathTemplates which we want to ignore here so it does not crash.
for (PathTemplate template : entry.getValue().getPathTemplates()) {
if (allMethodsMatcher.get(template.getTemplateString()) == null) {
if (allMethodsMatcher.match(template.getTemplateString()) == null) {
allMethodsMatcher.add(template, new RoutingMatch());
}
}
......
......@@ -24,6 +24,7 @@ import io.undertow.UndertowOptions;
import io.undertow.connector.PooledByteBuffer;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.UrlDecodeException;
import io.undertow.util.Headers;
import io.undertow.util.SameThreadExecutor;
import io.undertow.util.URLUtils;
......@@ -44,6 +45,7 @@ import java.nio.ByteBuffer;
public class FormEncodedDataDefinition implements FormParserFactory.ParserDefinition<FormEncodedDataDefinition> {
public static final String APPLICATION_X_WWW_FORM_URLENCODED = "application/x-www-form-urlencoded";
private static boolean parseExceptionLogAsDebug = false;
private String defaultEncoding = "ISO-8859-1";
private boolean forceCreation = false; //if the parser should be created even if the correct headers are missing
......@@ -143,7 +145,7 @@ public class FormEncodedDataDefinition implements FormParserFactory.ParserDefini
builder.setLength(0);
state = 2;
} else if (n == '&') {
data.add(builder.toString(), "");
addPair(builder.toString(), "");
builder.setLength(0);
state = 0;
} else if (n == '%' || n == '+') {
......@@ -156,11 +158,11 @@ public class FormEncodedDataDefinition implements FormParserFactory.ParserDefini
}
case 1: {
if (n == '=') {
name = URLUtils.decode(builder.toString(), charset, true, new StringBuilder());
name = decodeParameterName(builder.toString(), charset, true, new StringBuilder());
builder.setLength(0);
state = 2;
} else if (n == '&') {
data.add(URLUtils.decode(builder.toString(), charset, true, new StringBuilder()), "");
addPair(decodeParameterName(builder.toString(), charset, true, new StringBuilder()), "");
builder.setLength(0);
state = 0;
} else {
......@@ -170,7 +172,7 @@ public class FormEncodedDataDefinition implements FormParserFactory.ParserDefini
}
case 2: {
if (n == '&') {
data.add(name, builder.toString());
addPair(name, builder.toString());
builder.setLength(0);
state = 0;
} else if (n == '%' || n == '+') {
......@@ -183,7 +185,7 @@ public class FormEncodedDataDefinition implements FormParserFactory.ParserDefini
}
case 3: {
if (n == '&') {
data.add(name, URLUtils.decode(builder.toString(), charset, true, new StringBuilder()));
addPair(name, decodeParameterValue(name, builder.toString(), charset, true, new StringBuilder()));
builder.setLength(0);
state = 0;
} else {
......@@ -197,14 +199,14 @@ public class FormEncodedDataDefinition implements FormParserFactory.ParserDefini
} while (c > 0);
if (c == -1) {
if (state == 2) {
data.add(name, builder.toString());
addPair(name, builder.toString());
} else if (state == 3) {
data.add(name, URLUtils.decode(builder.toString(), charset, true, new StringBuilder()));
addPair(name, decodeParameterValue(name, builder.toString(), charset, true, new StringBuilder()));
} else if(builder.length() > 0) {
if(state == 1) {
data.add(URLUtils.decode(builder.toString(), charset, true, new StringBuilder()), "");
addPair(decodeParameterName(builder.toString(), charset, true, new StringBuilder()), "");
} else {
data.add(builder.toString(), "");
addPair(builder.toString(), "");
}
}
state = 4;
......@@ -215,6 +217,46 @@ public class FormEncodedDataDefinition implements FormParserFactory.ParserDefini
}
}
private void addPair(String name, String value) {
//if there was exception during decoding ignore the parameter [UNDERTOW-1554]
if(name != null && value != null) {
data.add(name, value);
}
}
private String decodeParameterValue(String name, String value, String charset, boolean decodeSlash, StringBuilder stringBuilder) {
String decodedValue = null;
try {
decodedValue = URLUtils.decode(value, charset, decodeSlash, stringBuilder);
} catch (UrlDecodeException e) {
if (!parseExceptionLogAsDebug) {
UndertowLogger.REQUEST_LOGGER.errorf(UndertowMessages.MESSAGES.failedToDecodeParameterValue(name, value, e));
parseExceptionLogAsDebug = true;
} else {
UndertowLogger.REQUEST_LOGGER.debugf(UndertowMessages.MESSAGES.failedToDecodeParameterValue(name, value, e));
}
}
return decodedValue;
}
private String decodeParameterName(String name, String charset, boolean decodeSlash, StringBuilder stringBuilder) {
String decodedName = null;
try {
decodedName = URLUtils.decode(name, charset, decodeSlash, stringBuilder);
} catch (UrlDecodeException e) {
if (!parseExceptionLogAsDebug) {
UndertowLogger.REQUEST_LOGGER.errorf(UndertowMessages.MESSAGES.failedToDecodeParameterName(name, e));
parseExceptionLogAsDebug = true;
} else {
UndertowLogger.REQUEST_LOGGER.debugf(UndertowMessages.MESSAGES.failedToDecodeParameterName(name, e));
}
}
return decodedName;
}
@Override
public void parse(HttpHandler handler) throws Exception {
......
/*
* JBoss, Home of Professional Open Source.
* Copyright 2019 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.util;
/**
* Created by Marek Marusic <mmarusic@redhat.com> on 7/25/19.
*/
public class UrlDecodeException extends IllegalArgumentException {
public UrlDecodeException(String message, Throwable cause) {
super(message, cause);
}
}
......@@ -126,6 +126,12 @@ public class RoutingHandlerTestCase {
exchange.getResponseSender().send("posted foo");
}
})
.add(Methods.POST, "/foo/{baz}", new HttpHandler() {
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
exchange.getResponseSender().send("foo-path" + exchange.getQueryParameters().get("bar"));
}
})
.add(Methods.GET, "/foo/{bar}", new HttpHandler() {
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
......
......@@ -42,7 +42,11 @@ import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
......@@ -118,29 +122,52 @@ public class FormDataParserTestCase {
@Test
public void testFormDataParsing() throws Exception {
runTest(new BasicNameValuePair("name", "A Value"));
runTest(new BasicNameValuePair("name", "A Value"), new BasicNameValuePair("Single-value", null));
runTest(new BasicNameValuePair("name", "A Value"), new BasicNameValuePair("A/name/with_special*chars", "A $ value&& with=SomeCharacters"));
runTest(new BasicNameValuePair("name", "A Value"), new BasicNameValuePair("Single-value", null) , new BasicNameValuePair("A/name/with_special*chars", "A $ value&& with=SomeCharacters"));
runTestUrlEncoded(new BasicNameValuePair("name", "A Value"));
runTestUrlEncoded(new BasicNameValuePair("name", "A Value"), new BasicNameValuePair("Single-value", null));
runTestUrlEncoded(new BasicNameValuePair("name", "A Value"), new BasicNameValuePair("A/name/with_special*chars", "A $ value&& with=SomeCharacters"));
runTestUrlEncoded(new BasicNameValuePair("name", "A Value"), new BasicNameValuePair("Single-value", null) , new BasicNameValuePair("A/name/with_special*chars", "A $ value&& with=SomeCharacters"));
}
@Test
public void testRawFormDataParsingIncorrectValue() throws Exception {
testRawFormDataParsing(new BasicNameValuePair("name", "%"));
testRawFormDataParsing(new BasicNameValuePair("Name%", "value"));
}
private void testRawFormDataParsing(NameValuePair wrongPair) throws Exception {
NameValuePair correctPair = new BasicNameValuePair("correctName", "A Value");
NameValuePair correctPair2 = new BasicNameValuePair("correctName2", "A Value2");
StringBuilder stringBuilder = new StringBuilder();
stringBuilder
.append(URLEncodedUtils.format(java.util.Collections.singleton(correctPair), HTTP.DEF_CONTENT_CHARSET))
.append("&")
.append(wrongPair.getName()).append("=").append(wrongPair.getValue())
.append("&")
.append(URLEncodedUtils.format(java.util.Collections.singleton(correctPair2), HTTP.DEF_CONTENT_CHARSET));
final List<NameValuePair> expectedData = new ArrayList<>();
expectedData.add(correctPair);
expectedData.add(correctPair2);
runTest(expectedData, new StringEntity(stringBuilder.toString(), ContentType.APPLICATION_FORM_URLENCODED));
}
private void runTest(final NameValuePair... pairs) throws Exception {
private void runTestUrlEncoded(final NameValuePair... pairs) throws Exception {
final List<NameValuePair> data = new ArrayList<>(Arrays.asList(pairs));
runTest(data, new UrlEncodedFormEntity(data));
}
private void runTest(List<NameValuePair> data, StringEntity entity) throws Exception {
DefaultServer.setRootHandler(rootHandler);
TestHttpClient client = new TestHttpClient();
try {
final List<NameValuePair> data = new ArrayList<>();
data.addAll(Arrays.asList(pairs));
HttpPost post = new HttpPost(DefaultServer.getDefaultServerURL() + "/path");
post.setHeader(Headers.CONTENT_TYPE_STRING, FormEncodedDataDefinition.APPLICATION_X_WWW_FORM_URLENCODED);
post.setEntity(new UrlEncodedFormEntity(data));
post.setEntity(entity);
HttpResponse result = client.execute(post);
Assert.assertEquals(StatusCodes.OK, result.getStatusLine().getStatusCode());
checkResult(data, result);
HttpClientUtils.readResponse(result);
} finally {
client.getConnectionManager().shutdown();
}
......@@ -153,6 +180,7 @@ public class FormDataParserTestCase {
res.put(split[0], split.length == 1 ? "" : split[1]);
}
Assert.assertEquals(data.size(), res.size());
for (NameValuePair vp : data) {
Assert.assertEquals(vp.getValue() == null ? "" : vp.getValue(), res.get(vp.getName()));
......
......@@ -3,7 +3,7 @@
<parent>
<groupId>io.undertow</groupId>
<artifactId>undertow-parent</artifactId>
<version>2.0.25.Final</version>
<version>2.0.26.Final</version>
</parent>
<artifactId>undertow-coverage-report</artifactId>
<name>Undertow Test Coverage Report</name>
......
undertow (2.0.26-1) unstable; urgency=medium
* New upstream version 2.0.26.
-- Markus Koschany <apo@debian.org> Wed, 11 Sep 2019 17:29:42 +0200
undertow (2.0.25-1) unstable; urgency=medium
* New upstream version 2.0.25.
......
......@@ -25,12 +25,12 @@
<parent>
<groupId>io.undertow</groupId>
<artifactId>undertow-parent</artifactId>
<version>2.0.25.Final</version>
<version>2.0.26.Final</version>
</parent>
<groupId>io.undertow</groupId>
<artifactId>undertow-dist</artifactId>
<version>2.0.25.Final</version>
<version>2.0.26.Final</version>
<name>Undertow: Distribution</name>
......
......@@ -25,12 +25,12 @@
<parent>
<groupId>io.undertow</groupId>
<artifactId>undertow-parent</artifactId>
<version>2.0.25.Final</version>
<version>2.0.26.Final</version>
</parent>
<groupId>io.undertow</groupId>
<artifactId>undertow-examples</artifactId>
<version>2.0.25.Final</version>
<version>2.0.26.Final</version>
<name>Undertow Examples</name>
......
......@@ -25,12 +25,12 @@
<parent>
<groupId>io.undertow</groupId>
<artifactId>undertow-parent</artifactId>
<version>2.0.25.Final</version>
<version>2.0.26.Final</version>
</parent>
<groupId>io.undertow</groupId>
<artifactId>karaf</artifactId>
<version>2.0.25.Final</version>
<version>2.0.26.Final</version>
<packaging>pom</packaging>
<name>Undertow Karaf Features</name>
......
......@@ -25,12 +25,12 @@
<parent>
<groupId>io.undertow</groupId>
<artifactId>undertow-parent</artifactId>
<version>2.0.25.Final</version>
<version>2.0.26.Final</version>
</parent>
<groupId>io.undertow</groupId>
<artifactId>undertow-parser-generator</artifactId>
<version>2.0.25.Final</version>
<version>2.0.26.Final</version>
<name>Undertow Parser Generator</name>
<description>An annotation processor that is used to generate the HTTP parser</description>
......
......@@ -28,7 +28,7 @@
<groupId>io.undertow</groupId>
<artifactId>undertow-parent</artifactId>
<version>2.0.25.Final</version>
<version>2.0.26.Final</version>
<name>Undertow</name>
<description>Undertow</description>
......
......@@ -25,12 +25,12 @@
<parent>
<groupId>io.undertow</groupId>
<artifactId>undertow-parent</artifactId>
<version>2.0.25.Final</version>
<version>2.0.26.Final</version>
</parent>
<groupId>io.undertow</groupId>
<artifactId>undertow-servlet</artifactId>
<version>2.0.25.Final</version>
<version>2.0.26.Final</version>
<name>Undertow Servlet</name>
......
......@@ -20,6 +20,7 @@ package io.undertow.servlet.api;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
......@@ -655,6 +656,18 @@ public class DeploymentInfo implements Cloneable {
return tempDir;
}
/**
* @return Returns the {@link #getTempDir() temp directory path} if it's
* not null, else returns the system level temporary directory path
* pointed to by the Java system property {@code java.io.tmpdir}
*/
public Path requireTempPath() {
if (tempDir != null) {
return tempDir;
}
return Paths.get(SecurityActions.getSystemProperty("java.io.tmpdir"));
}
public DeploymentInfo setTempDir(final File tempDir) {
this.tempDir = tempDir != null ? tempDir.toPath() : null;
return this;
......