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());
+ }
+}