From ed9628bb6d0ac5647475d56d514d474af654bfe8 Mon Sep 17 00:00:00 2001 From: Edgar Espina Date: Sun, 19 Feb 2017 15:27:25 -0300 Subject: [PATCH] cookie parser fails when cookie value isn't present fix #649 --- jooby/src/main/java/org/jooby/Cookie.java | 48 +++++++++++-------- .../jooby/internal/CookieSessionManager.java | 20 ++++---- .../main/java/org/jooby/issues/Issue649.java | 16 +++++++ 3 files changed, 52 insertions(+), 32 deletions(-) create mode 100644 jooby/src/main/java/org/jooby/issues/Issue649.java diff --git a/jooby/src/main/java/org/jooby/Cookie.java b/jooby/src/main/java/org/jooby/Cookie.java index cece3f3309..3d266c3160 100644 --- a/jooby/src/main/java/org/jooby/Cookie.java +++ b/jooby/src/main/java/org/jooby/Cookie.java @@ -7,7 +7,7 @@ * "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 + * 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 @@ -18,30 +18,29 @@ */ package org.jooby; -import static java.util.Objects.requireNonNull; +import com.google.common.base.Splitter; +import com.google.common.base.Strings; +import com.google.common.io.BaseEncoding; +import javaslang.Tuple; +import javaslang.Tuple2; +import javaslang.control.Try; +import org.jooby.internal.CookieImpl; +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import java.util.Collections; import java.util.Iterator; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.function.Function; import java.util.regex.Pattern; import java.util.stream.Collectors; -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; - -import org.jooby.internal.CookieImpl; - -import com.google.common.base.Splitter; -import com.google.common.base.Strings; -import com.google.common.io.BaseEncoding; - -import javaslang.Tuple; -import javaslang.Tuple2; -import javaslang.control.Try; +import static java.util.Objects.requireNonNull; /** * Creates a cookie, a small amount of information sent by a server to @@ -84,18 +83,27 @@ public interface Cookie { * {@link URLDecoder}. */ public static final Function> URL_DECODER = value -> { + if (value == null) { + return Collections.emptyMap(); + } Function decode = v -> Try - .of(() -> URLDecoder.decode(v, StandardCharsets.UTF_8.name())).get(); - return Splitter.on('&').trimResults().omitEmptyStrings() + .of(() -> URLDecoder.decode(v, StandardCharsets.UTF_8.name())) + .get(); + return Splitter.on('&') + .trimResults() + .omitEmptyStrings() .splitToList(value) .stream() .map(v -> { Iterator it = Splitter.on('=').trimResults().omitEmptyStrings() .split(v) .iterator(); - Tuple2 t2 = Tuple.of(decode.apply(it.next()), decode.apply(it.next())); + Tuple2 t2 = Tuple + .of(decode.apply(it.next()), it.hasNext() ? decode.apply(it.next()) : null); return t2; - }).collect(Collectors.toMap(it -> it._1, it -> it._2)); + }) + .filter(it -> Objects.nonNull(it._2)) + .collect(Collectors.toMap(it -> it._1, it -> it._2)); }; /** @@ -349,7 +357,7 @@ public Optional secure() { *

* * @param maxAge an integer specifying the maximum age of the cookie in seconds; if negative, - * means the cookie is not stored; if zero, deletes the cookie. + * means the cookie is not stored; if zero, deletes the cookie. * @return This definition. */ public Definition maxAge(final int maxAge) { @@ -494,7 +502,7 @@ public static boolean valid(final String value, final String secret) { *

* * @return An integer specifying the maximum age of the cookie in seconds; if negative, means - * the cookie persists until browser shutdown + * the cookie persists until browser shutdown */ int maxAge(); diff --git a/jooby/src/main/java/org/jooby/internal/CookieSessionManager.java b/jooby/src/main/java/org/jooby/internal/CookieSessionManager.java index dfb5fcf858..756441807c 100644 --- a/jooby/src/main/java/org/jooby/internal/CookieSessionManager.java +++ b/jooby/src/main/java/org/jooby/internal/CookieSessionManager.java @@ -7,7 +7,7 @@ * "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 + * 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 @@ -18,14 +18,6 @@ */ package org.jooby.internal; -import java.util.Collections; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; -import javax.inject.Named; - import org.jooby.Cookie; import org.jooby.Cookie.Definition; import org.jooby.Request; @@ -36,6 +28,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.inject.Inject; +import javax.inject.Named; +import java.util.Collections; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + /** * Save session data in a cookie. * @@ -100,9 +99,6 @@ public Definition cookie() { private Map attributes(final String raw) { String unsigned = Cookie.Signature.unsign(raw, secret); - if (unsigned == null) { - return Collections.emptyMap(); - } return Cookie.URL_DECODER.apply(unsigned); } diff --git a/jooby/src/main/java/org/jooby/issues/Issue649.java b/jooby/src/main/java/org/jooby/issues/Issue649.java new file mode 100644 index 0000000000..a28d7b3de7 --- /dev/null +++ b/jooby/src/main/java/org/jooby/issues/Issue649.java @@ -0,0 +1,16 @@ +package org.jooby.issues; + +import org.jooby.Cookie; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +public class Issue649 { + + @Test + public void emptyCookie() { + assertTrue(Cookie.URL_DECODER.apply("foo=").isEmpty()); + assertTrue(Cookie.URL_DECODER.apply("foo").isEmpty()); + assertTrue(Cookie.URL_DECODER.apply(null).isEmpty()); + } +}