aboutsummaryrefslogtreecommitdiffstats
path: root/container-core/src/main/java/com/yahoo/jdisc/http/Cookie.java
diff options
context:
space:
mode:
Diffstat (limited to 'container-core/src/main/java/com/yahoo/jdisc/http/Cookie.java')
-rw-r--r--container-core/src/main/java/com/yahoo/jdisc/http/Cookie.java250
1 files changed, 250 insertions, 0 deletions
diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/Cookie.java b/container-core/src/main/java/com/yahoo/jdisc/http/Cookie.java
new file mode 100644
index 00000000000..d882cf7a34a
--- /dev/null
+++ b/container-core/src/main/java/com/yahoo/jdisc/http/Cookie.java
@@ -0,0 +1,250 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.jdisc.http;
+
+import org.eclipse.jetty.http.HttpCookie;
+import org.eclipse.jetty.server.CookieCutter;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+
+import static java.util.stream.Collectors.toList;
+
+/**
+ * A RFC 6265 compliant cookie.
+ *
+ * Note: RFC 2109 and RFC 2965 is no longer supported. All fields that are not part of RFC 6265 are deprecated.
+ *
+ * @author Einar M R Rosenvinge
+ * @author bjorncs
+ */
+public class Cookie {
+
+ private final Set<Integer> ports = new HashSet<>();
+ private String name;
+ private String value;
+ private String domain;
+ private String path;
+ private SameSite sameSite;
+ private long maxAgeSeconds = Integer.MIN_VALUE;
+ private boolean secure;
+ private boolean httpOnly;
+ private boolean discard;
+
+ public Cookie() {
+ }
+
+ public Cookie(Cookie cookie) {
+ ports.addAll(cookie.ports);
+ name = cookie.name;
+ value = cookie.value;
+ domain = cookie.domain;
+ path = cookie.path;
+ sameSite = cookie.sameSite;
+ maxAgeSeconds = cookie.maxAgeSeconds;
+ secure = cookie.secure;
+ httpOnly = cookie.httpOnly;
+ discard = cookie.discard;
+ }
+
+ public Cookie(String name, String value) {
+ this.name = name;
+ this.value = value;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Cookie setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public Cookie setValue(String value) {
+ this.value = value;
+ return this;
+ }
+
+ public String getDomain() {
+ return domain;
+ }
+
+ public Cookie setDomain(String domain) {
+ this.domain = domain;
+ return this;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public Cookie setPath(String path) {
+ this.path = path;
+ return this;
+ }
+
+ public SameSite getSameSite() {
+ return sameSite;
+ }
+
+ public Cookie setSameSite(SameSite sameSite) {
+ this.sameSite = sameSite;
+ return this;
+ }
+
+ public int getMaxAge(TimeUnit unit) {
+ return (int)unit.convert(maxAgeSeconds, TimeUnit.SECONDS);
+ }
+
+ public Cookie setMaxAge(int maxAge, TimeUnit unit) {
+ this.maxAgeSeconds = maxAge >= 0 ? unit.toSeconds(maxAge) : Integer.MIN_VALUE;
+ return this;
+ }
+
+ public boolean isSecure() {
+ return secure;
+ }
+
+ public Cookie setSecure(boolean secure) {
+ this.secure = secure;
+ return this;
+ }
+
+ public boolean isHttpOnly() {
+ return httpOnly;
+ }
+
+ public Cookie setHttpOnly(boolean httpOnly) {
+ this.httpOnly = httpOnly;
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Cookie cookie = (Cookie) o;
+ return maxAgeSeconds == cookie.maxAgeSeconds &&
+ secure == cookie.secure &&
+ httpOnly == cookie.httpOnly &&
+ discard == cookie.discard &&
+ sameSite == cookie.sameSite &&
+ Objects.equals(ports, cookie.ports) &&
+ Objects.equals(name, cookie.name) &&
+ Objects.equals(value, cookie.value) &&
+ Objects.equals(domain, cookie.domain) &&
+ Objects.equals(path, cookie.path);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(ports, name, value, domain, path, sameSite, maxAgeSeconds, secure, httpOnly, discard);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder ret = new StringBuilder();
+ ret.append(name).append("=").append(value);
+ return ret.toString();
+ }
+ // NOTE cookie encoding and decoding:
+ // The implementation uses Jetty for server-side (encoding of Set-Cookie and decoding of Cookie header),
+ // and java.net.HttpCookie for client-side (encoding of Cookie and decoding of Set-Cookie header).
+ //
+ // Implementation is RFC-6265 compliant.
+
+ public static String toCookieHeader(Iterable<? extends Cookie> cookies) {
+ return StreamSupport.stream(cookies.spliterator(), false)
+ .map(cookie -> {
+ java.net.HttpCookie httpCookie = new java.net.HttpCookie(cookie.getName(), cookie.getValue());
+ httpCookie.setDomain(cookie.getDomain());
+ httpCookie.setHttpOnly(cookie.isHttpOnly());
+ httpCookie.setMaxAge(cookie.getMaxAge(TimeUnit.SECONDS));
+ httpCookie.setPath(cookie.getPath());
+ httpCookie.setSecure(cookie.isSecure());
+ httpCookie.setVersion(0);
+ return httpCookie.toString();
+ })
+ .collect(Collectors.joining(";"));
+ }
+
+ public static List<Cookie> fromCookieHeader(String headerVal) {
+ CookieCutter cookieCutter = new CookieCutter();
+ cookieCutter.addCookieField(headerVal);
+ return Arrays.stream(cookieCutter.getCookies())
+ .map(servletCookie -> {
+ Cookie cookie = new Cookie();
+ cookie.setName(servletCookie.getName());
+ cookie.setValue(servletCookie.getValue());
+ cookie.setPath(servletCookie.getPath());
+ cookie.setDomain(servletCookie.getDomain());
+ cookie.setMaxAge(servletCookie.getMaxAge(), TimeUnit.SECONDS);
+ cookie.setSecure(servletCookie.getSecure());
+ cookie.setHttpOnly(servletCookie.isHttpOnly());
+ return cookie;
+ })
+ .collect(toList());
+ }
+
+ public static List<String> toSetCookieHeaders(Iterable<? extends Cookie> cookies) {
+ return StreamSupport.stream(cookies.spliterator(), false)
+ .map(cookie ->
+ new org.eclipse.jetty.http.HttpCookie(
+ cookie.getName(),
+ cookie.getValue(),
+ cookie.getDomain(),
+ cookie.getPath(),
+ cookie.getMaxAge(TimeUnit.SECONDS),
+ cookie.isHttpOnly(),
+ cookie.isSecure(),
+ null, /* comment */
+ 0, /* version */
+ Optional.ofNullable(cookie.getSameSite()).map(SameSite::jettySameSite).orElse(null)
+ ).getRFC6265SetCookie())
+ .collect(toList());
+ }
+
+ @Deprecated // TODO Vespa 8 Remove
+ public static List<String> toSetCookieHeaderAll(Iterable<? extends Cookie> cookies) {
+ return toSetCookieHeaders(cookies);
+ }
+
+ public static Cookie fromSetCookieHeader(String headerVal) {
+ return java.net.HttpCookie.parse(headerVal).stream()
+ .map(httpCookie -> {
+ Cookie cookie = new Cookie();
+ cookie.setName(httpCookie.getName());
+ cookie.setValue(httpCookie.getValue());
+ cookie.setDomain(httpCookie.getDomain());
+ cookie.setHttpOnly(httpCookie.isHttpOnly());
+ cookie.setMaxAge((int) httpCookie.getMaxAge(), TimeUnit.SECONDS);
+ cookie.setPath(httpCookie.getPath());
+ cookie.setSecure(httpCookie.getSecure());
+ return cookie;
+ })
+ .findFirst().get();
+ }
+
+ public enum SameSite {
+ NONE, STRICT, LAX;
+
+ HttpCookie.SameSite jettySameSite() {
+ return HttpCookie.SameSite.valueOf(name());
+ }
+
+ static SameSite fromJettySameSite(HttpCookie.SameSite jettySameSite) {
+ return valueOf(jettySameSite.name());
+ }
+ }
+}